在邻接列表上具有CRUD操作的分层网格(具有ParentId列的表)

时间:2013-09-20 16:20:00

标签: c# sql-server asp.net-mvc-4 crud hierarchical-data

我正在使用ASP.NET MVC4。我完全不知道从哪里开始 - 我需要在每个级别显示一个完整的CRUD操作的分层网格(编辑行,创建行,删除行)。我正在使用实体框架(.edmx)的数据库优先方法。实体之间的关系由我的数据库中同一行中的“ParentId”值维护。我希望将“删除”功能级联到所有子级,就像在SQL Server中的关系设置中一样。

enter image description here

问题:如果我想使用ParentId的概念来维护层次关系,我是否想要开始使用CRUD操作完成分层网格?我查看了ListViews,我认为这不是我需要的。除非绝对必要,否则我不想过于复杂化,我宁愿不采用商品解决方案。

2 个答案:

答案 0 :(得分:2)

我最终使用了MvcTreeView(http://mvctreeview.codeplex.com/)。这也可以在Nuget中找到。此Html Helper旨在专门用于邻接列表(通过ParentId列维护其层次结构的分层表,或类似的)。此帮助程序生成表中每条记录的分层树视图。此外,您可以通过在每个项目上添加单独的CRUD操作来进一步自定义,并且CRUD将像通常的ASP.NET MVC w / Entity Framework项目中一样工作。

要在“n”深度的树下一直级联删除,这里是如何做到的:

我最终创建了一个专门用于邻接列表的自定义模块。 SQL中的邻接列表是一个表,它通过一列ParentId维护一个“n”深度层次结构,该列是返回同一表中Id列(主键)的外键。

我创建了一个名为“Children”的帮助器类。此类的目的是返回给定父级的所有遍历子Id的列表。因此,如果你传递Id为父级提高6级,下面的代码将遍历所有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;
        }
    }

答案 1 :(得分:0)

https://datatables.net/可能是一个好的开始。检查编辑器插件。