我正在尝试使用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]个子分支。
我有两个问题:
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()方法中收到一个异常。错误消息显示:&#34;值不能为空。 参数名称:source&#34;。不幸的是,这并不能让我知道这里出了什么问题。
我希望有人可以帮助我吗?非常感谢!
答案 0 :(得分:9)
异常是由Select
集合上的SelectMany
或null
引起的,在您的情况下是
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将执行填充集合的查询。如果没有子节点,则集合将为空,而不是空。
那么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(公用表表达式)的视图。但这是一个不同的故事......