如何使用Entity框架进行递归加载?

Avi*_*ush 20 recursion entity load entity-framework

我在带有TreeNodes表的DB中有一个树结构.该表有nodeId,parentId和parameterId.在EF中,结构类似于TreeNode.Children,其中每个子节点都是TreeNode ...我还有一个包含id,name和rootNodeId的Tree表.

在一天结束时,我想将树加载到TreeView中,但我无法想象如何一次加载它.我试过了:

var trees = from t in context.TreeSet.Include("Root").Include("Root.Children").Include("Root.Children.Parameter")
        .Include("Root.Children.Children")
                        where t.ID == id
                        select t;
Run Code Online (Sandbox Code Playgroud)

这将使我成为前两代而不是更多.如何使用所有代和其他数据加载整个树?

Joe*_*aus 15

我最近遇到了这个问题,在找到一个简单的方法来实现结果之后偶然发现了这个问题.我提供了一个编辑克雷格的答案,提供了第四种方法,但权力 - 决定它应该是另一个答案.这对我来说没问题 :)

我的原始问题/答案可以在这里找到.

只要表中的项目都知道它们属于哪个树(在您的情况下看起来像它们那样t.ID),这就可以工作.也就是说,目前尚不清楚你真正拥有的实体,但即使你有多个实体,你必须在实体中拥有一个FK,Children如果那不是TreeSet

基本上,只是不要使用Include():

var query = from t in context.TreeSet
            where t.ID == id
            select t;

// if TreeSet.Children is a different entity:
var query = from c in context.TreeSetChildren
            // guessing the FK property TreeSetID
            where c.TreeSetID == id
            select c;
Run Code Online (Sandbox Code Playgroud)

这将返回树的所有项目,并将它们全部放在集合的根目录中.此时,您的结果集将如下所示:

-- Item1
   -- Item2
      -- Item3
-- Item4
   -- Item5
-- Item2
-- Item3
-- Item5
Run Code Online (Sandbox Code Playgroud)

既然你可能希望你的实体只是分层次地从EF出来,这不是你想要的,对吧?

..然后,排除存在于根级别的后代:

幸运的是,因为模型中有导航属性,所以仍然会填充子实体集合,如上面结果集的图示所示.通过使用循环手动迭代结果集foreach(),并将这些根项添加到a new List<TreeSet>(),您现在将拥有一个包含根元素的列表,并且所有后代都已正确嵌套.

如果您的树木变大并且性能受到关注,您可以将返回集ASCENDING排序ParentID(对Nullable,对吗?),以便所有根项都是第一个.像以前一样迭代和添加,但是一旦你得到一个非空的,就会从循环中断开.

var subset = query
     // execute the query against the DB
     .ToList()
     // filter out non-root-items
     .Where(x => !x.ParentId.HasValue);
Run Code Online (Sandbox Code Playgroud)

现在subset看起来像这样:

-- Item1
   -- Item2
      -- Item3
-- Item4
   -- Item5
Run Code Online (Sandbox Code Playgroud)



关于克雷格的解决方案:

  1. 真的不想使用延迟加载!围绕n + 1查询的必要性而设计的设计将成为主要的性能吸引力.
  2. *********(嗯,公平地说,如果你要允许用户有选择地向下钻取树,那么它可能是合适的.只是不要使用延迟加载来使它们全部运行起来前线!!)

  3. 我从来没有尝试过嵌套的设置,我也不会建议破解EF配置来使这项工作,因为有一个更容易的解决方案.

  4. 另一个合理的建议是创建一个提供自链接的数据库视图,然后将该视图映射到中间连接/链接/ m2m表.就个人而言,我发现这个解决方案比必要的更复杂,但它可能有它的用途.

  • 对我投降的人会不会解释为什么? (3认同)

Cra*_*ntz 4

当您使用 时Include(),您要求实体框架将您的查询转换为 SQL。那么想一想:如何编写返回任意深度的树的 SQL 语句?

答:除非您使用数据库服务器的特定层次结构功能(这不是 SQL 标准,但某些服务器支持,例如 SQL Server 2008,但其实体框架提供程序不支持),否则您不会这样做。在 SQL 中处理任意深度的树的常用方法是使用嵌套集模型而不是父 ID 模型。

因此,您可以使用三种方法来解决此问题:

  1. 使用嵌套集模型。这需要更改您的元数据。
  2. 使用 SQL Server 的层次结构功能,并破解实体框架以理解它们(很棘手,但这种技术可能有效)。同样,您需要更改元数据。i
  3. 使用显式加载或 EF 4 的延迟加载而不是预先加载。这将导致许多数据库查询,而不是一个。

  • 最后,我添加了一个递归调用,该调用对 Children 和其他引用的对象调用 Load。谢谢 (2认同)