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

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (30)

我在具有TreeNodes表的数据库中有一个树结构。该表具有nodeId,parentId和parameterId。在EF中,结构就像TreeNode.Children,其中每个孩子都是TreeNode ...我也有一个Tree表,其中包含id,name和rootNodeId。

在一天结束时,我想将树加载到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;

这将使我获得前两代,但不会更多。如何加载整个树的所有世代和额外的数据?

提问于
用户回答回答于

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

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

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

  1. 使用嵌套集模型。这需要更改您的元数据。
  2. 使用SQL Server的层次结构功能,并攻击实体框架来理解它们。同样,你需要改变你的metadata.i
  3. 使用显式加载或EF 4的延迟加载而不是急切的加载。这将导致许多数据库查询而不是一个。
用户回答回答于

这工作只要在表格中的项目都知道自己属于哪个树(这在你的情况下,它看起来像他们这样做:t.ID)。也就是说,目前还不清楚你真的有什么实体,但即使你有不止一个实体,Children如果这不是一个实体,你也必须在实体中拥有一个FKTreeSet

基本上,只是不要使用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;

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

-- Item1
   -- Item2
      -- Item3
-- Item4
   -- Item5
-- Item2
-- Item3
-- Item5

因为你很可能希望你的实体出来EF的唯一层次,这是不是你想要的,对不对?

那么,排除在根级别存在的后代:

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

如果你的树变大并且性能是一个问题,你可以通过ParentID(它的Nullable正确的?)对返回集ASCENDING进行排序,以便所有的根项都是第一个。迭代和添加如前所述,但是一旦遇到非空的循环,就从循环中断开。

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

现在subset看起来像这样:

-- Item1
   -- Item2
      -- Item3
-- Item4
   -- Item5

关于Craig的解决方案:

  1. 真的不想使用懒加载!围绕n + 1查询的必要性构建的设计将成为主要的性能吸引者。

*********(公平地说,如果你打算允许用户选择性地钻取树,那么它可能是合适的,只是不要使用延迟加载来全部获取它们!!

  1. 我从来没有尝试嵌套设置的东西,我不会建议黑客EF配置,以使这项工作,既然有一个更容易的解决方案。

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

扫码关注云+社区