gpt4 book ai didi

entity-framework - 与 Code First 的一对多递归关系

转载 作者:行者123 更新时间:2023-12-04 08:33:52 24 4
gpt4 key购买 nike

我正在尝试使用 EF 6.1.2 Code First 实现一个简单的自引用关系。

public class Branch 
{
[Key]
public int Id { get; set; }

[Required]
public string Name { get; set; }

public int? ParentId { get; set; }

[ForeignKey("ParentId")]
public virtual Branch Parent { get; set; }

public ICollection<Branch> Children { get; set; } // direct successors
}

在我的应用程序中,我只有一个根分支。除了这个单一的根分支之外,每个分支都只有一个父分支(根分支的 parentId 为 NULL)。除此之外,每个分支都可以有 [0..n] 个子分支。

我有两个问题:
  • 我是否需要在 OnModelCreating(DbModelBuilder modelBuilder) 中指定任何额外的 FluentApi 代码才能让 EF 理解这种一对多的自引用关系?我试过这个:modelBuilder.Entity<Branch>().HasOptional<Branch>(b => b.Parent).WithMany(b => b.Children).HasForeignKey(b => b.ParentId);但我不确定我是否需要这个。
  • 对于给定的分支,我想检索所有子项(在层次结构中一直向下)。这是我目前想到的:

  • .
     public IEnumerable<Branch> GetBranches(Branch anyBranch)
    {
    return anyBranch.Flatten(b => b.Children);
    }


     public static IEnumerable<T> Flatten<T>(this T node, Func<T, IEnumerable<T>> selector)
    {
    return selector(node).SelectMany(x => Flatten(x, selector))
    .Concat(new[] { node });
    }

    第二个片段不是我的。我在 StackOverflow 上的其他地方找到了它。老实说,我几乎不明白它应该如何工作。

    当我运行我的应用程序并调用 GetBranches()(我在几个不同的分支上尝试过这个)时,我在 Flatten() 方法中收到一个异常。错误消息说:“值不能为空。
    参数名称:源”。不幸的是,这并没有给我任何线索这里出了什么问题。

    我希望有人可以帮助我吗?非常感谢!

    最佳答案

    异常的原因

    异常是由 Select 引起的或 SelectManynull收集,在你的情况下的结果

    b => b.Children

    对于层次结构中的每个分支, Children当他们到达零件时访问集合
    selector(node)
    selector是 lambda 表达式 b => b.Children ,这与方法相同
    IEnumerable<Branch> anonymousMethod(Branch b)
    {
    return b.Children;
    }

    所以实际发生的是 b.Children.SelectMany(...) , 或 null.SelectMany(...) ,这会引发您看到的异常。

    预防它

    但为什么是这些 Children集合为空?

    这是因为不会发生延迟加载。要启用延迟加载,集合必须是 virtual :
    public virtual ICollection<Branch> Children { get; set; }

    当 EF 获取 Branch从数据库中创建一个对象 proxy对象,派生自 Branch 的对象,通过能够延迟加载的代码覆盖虚拟属性。现在当 b.Children已解决,EF 将执行填充集合的查询。如果没有 child ,则集合将为空,而不是 null。

    扁平化解释

    那么在 Flatten 中会发生什么?方法是首先获取分支的子节点( selector(node) ),然后在每个子节点( SelectMany )上获取 Flatten方法再次被调用(现在就像一个方法 Flatten(x, selector) ,而不是一个扩展方法)。

    Flatten方法每个节点都被添加到其子节点的集合( .Concat(new[] { node }) ,所以最后,层次结构中的所有节点都被返回(因为 Flatten 返回进入它的节点)。

    一些备注
  • 我希望在集合顶部有父节点,所以我会更改 Flatten方法进入
    public static IEnumerable<T> Flatten<T>(this T node, Func<T,IEnumerable<T>> selector)
    {
    return new[] { node }
    .Concat(selector(node).SelectMany(x => Flatten(x, selector)));
    }
  • 通过延迟加载获取层次结构非常低效。事实上,LINQ 并不是最适合查询层次结构的工具。有效地执行此操作需要数据库中使用 CTE(公用表表达式)的 View 。但那是另一回事了...
  • 关于entity-framework - 与 Code First 的一对多递归关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27720369/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com