我最终创建了一个专门用于邻接列表的自定义模块。SQL 中的邻接列表是您有一个表维护“n”深度层次结构,其中 ParentId 列是返回到同一表中 Id 列(主键)的外键。
我创建了一个名为“Children”的助手类。此类的目的是返回给定父级的所有遍历的子级 ID 的列表。因此,如果您传入 6 级以上的父母的 Id,下面的代码将遍历所有 6 级并返回所有孩子、孙子、曾孙等的 Id 的完整列表。
我得到所有 Id 的子项列表而不是要删除的对象列表的原因是,如果我得到要删除的对象列表,我将不得不在不同的 DbContext 下获取这些对象,所以当我尝试实际删除它们,我会收到一条错误消息,指出该对象已分离(或类似的东西),因为它是在不同的上下文中实例化的。获取 ID 列表可以防止这种情况发生,我们可以使用相同的上下文来执行删除。
因此,从您的代码中调用此方法GetChildrenIds(List<int> immediateChildrenIds)
并传入与ints
所选节点的直接子节点相对应的列表。例如,要获取要传递给此方法的直接子级列表,请使用类似以下的内容(这是基于使用 WebAPI 的 ASP.NET MVC 模式):
// keep in mind that `id` is the `id` of the clicked on node.
// DELETE api/region/5
public HttpResponseMessage Delete(int id)
{
Region region = db.Regions.Find(id);
List<int> tempRegionIds = new List<int>();
List<int> immediateChildrenIds = (from i in db.Regions where i.ParentId == id select i.Id).ToList();
List<int> regionsToDeleteIds = new List<int>();
// the below is needed because we need to add the Id of the clicked on node to make sure it gets deleted as well
regionsToDeleteIds.Add(region.Id);
// we can't make this a static method because that would require static member
// variables, and static member variables persist throughout each recursion
HelperClasses.HandleChildren.Children GetChildren = new HelperClasses.HandleChildren.Children();
// see below this code block for the GetChildrenIds(...) method
tempRegionIds = GetChildren.GetChildrenIds(immediateChildrenIds);
// here, we're just adding to regionsToDeleteIds the children of the traversed parent
foreach (int tempRegionId in tempRegionIds)
{
regionsToDeleteIds.Add(tempRegionId);
}
// reverse the order so it goes youngest to oldest (child to grandparent)
// is it necessary? I don't know honestly. I just wanted to make sure that
// the lowest level child got deleted first (the one that didn't have any children)
regionsToDeleteIds.Reverse(0, regionsToDeleteIds.Count);
if (regionsToDeleteIds == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
foreach (int regionId in regionsToDeleteIds)
{
// here we're finding the object based on the passed in Id and deleting it
Region deleteRegion = db.Regions.Find(regionId);
db.Regions.Remove(deleteRegion);
}
...
下面的代码是返回子 ID 的完整列表的类。我把这段代码放在一个单独的帮助类文件中。DbContext_db
是我所说的,当我说您不想在此上下文下检索对象列表时,否则Delete
在控制器中实际调用它时将无法工作。因此,如您所见,我得到了一个 Id 列表,并进行了实际的 DbContext 调用以获取我的控制器中的对象,而不是这个帮助程序类。
public class Children
{
private Entities _db = new Entities(HelperClasses.DBHelper.GetConnectionString());
private List<int> _childrenIds = new List<int>();
private List<int> _childRegionIds = new List<int>();
public List<int> GetChildrenIds(List<int> immediateChildrenIds)
{
// traverse the immediate children
foreach (var i in immediateChildrenIds)
{
_childRegionIds.Add(i);
_childrenIds = (from child in _db.Regions where child.ParentId == i select child.Id).ToList();
if (_childrenIds.Any())
GetChildrenIds(_childrenIds);
else
continue;
}
return _childRegionIds;
}
}