实体框架4.1中反向关联同步指南

8
EF 4.1 可以在您创建实例时同步反向关联。是否有任何有关此行为的文档或最佳实践指南可用?
所谓“同步反向关联”,是指:给定:
public class Blog
{
   public Blog() { Posts = new List<Blog>(); }
   public int Id { get; set; }
   public ICollection<Post> Posts { get; private set; }
}

public class Post
{
   public Blog Blog { get; set; }
   public int Id { get; set; }
}

接下来的代码中,一旦执行了以下这行代码,该 Post 的 Blog 属性就会被设置。

var blog = new Blog();
context.Blogs.Add(blog);
blog.Posts.Add(new Post());
1个回答

9
我认为你所说的“synchronising the reverse association”可能是Entity Framework中称为“Relationship Fix-up”或“Relationship Span”的功能,它负责在ObjectContext中自动分配对象之间的导航属性。这不仅适用于EF 4.1,也适用于旧版本。
我不知道这个功能的全面文档,但以下资源可能会提供更多见解,特别是第二个:
- 简要定义:http://blogs.msdn.com/b/alexj/archive/2009/04/03/tip-10-understanding-entity-framework-jargon.aspx - 更详细的解释(Zeeshan Hirani):http://www.daltinkurt.com/upload/dosyalar/file/Diger/entity_framework_learning_guide.pdf(第3.4章,第125-133页) - 关于想避免关系跨度的情况:http://blogs.msdn.com/b/alexj/archive/2009/04/07/tip-11-avoiding-relationship-span.aspx 编辑:
我无法给出关于关系跨度及其所有影响的全面解释。但我可以尝试给出一些例子,我相信我的说法不是完全错误的:
在你评论中链接的答案中,Morteza区分了派生自EntityObject的实体(仅适用于EF 4.0中的ObjectContext,在EF 4.1中的DbContext不可用)和POCOs(在ObjectContext和DbContext中均可用)。
如果您有POCOs,则将新对象添加到已加载到上下文中的另一个对象的导航集合中不会将新对象附加到上下文中。这并不奇怪,因为POCOs是POCOs,这意味着它们对EF上下文一无所知。将对象添加到导航集合中实际上只是类似于List<T>.Add(...)这样的操作。这个通用的Add方法不会对EF上下文进行任何操作。

这是涉及到EntityObjectEntityCollection的另一种情况,它们都在内部引用了上下文,并且因此当您将其添加到集合时可以立即附加到上下文。

从这个考虑中得出的一个结论是,在使用POCO时,您的问题中的最后一个代码示例实际上不会设置Post中的Blog属性。但是:在调用DetectChangesSaveChanges(它在内部调用DetectChanges)之后,它将被设置。在这种情况下,DetectChanges(可能是一个非常复杂的方法)查看上下文中有哪些对象(它会找到Blog父对象),然后遍历整个对象图(在我们的例子中是Posts集合),并检查图中的其他对象(Post对象)是否也在上下文中。如果没有 - 这就是您的例子中的情况 - 它将以Added状态将它们附加到上下文中,并且现在出现了关系跨度 - 也会修复对象图中的导航属性。

关系跨度还在加载对象到上下文时与POCO一起使用的另一种情况。

例如:如果您在数据库中有一个id = x的Blog和一个属于此Blog的id = y的Post,则此代码...

var blog = context.Blogs.Find(x); // no eager loading of the Posts collection!
var post = context.Posts.Find(y); // no eager loading of the Blog property!

会自动在每个对象中建立导航属性,因此 Blog 的 Posts 集合将突然包含帖子,并且 Post 中的 Blog 属性将引用该博客。这种关系修复取决于对象确实加载到上下文中。如果您例如使用 AsNoTracking 抑制了这一点...
var blog = context.Blogs.AsNoTracking().Where(b => b.Id == x).Single();
var post = context.Posts.AsNoTracking().Where(p => p.Id == y).Single();

注意:如果关联的至少一端的基数为0...1(一对一或一对多关联),则像上面的示例中那样的关系跨度才有效。对于多对多关联,它永远不起作用。最近在这里讨论过这个问题(使用EF 4.1):EF 4.1 loading filtered child collections not working for many-to-many

另外一点:关系跨度无法工作,并且导航属性将保持null


谢谢Slauma,我之前没有听说过“关系跨度”这个术语。这对于寻找有关此行为的材料非常有帮助。在阅读了您提供的链接并搜索了其他信息后,我发现了这个SO答案:http://goo.gl/4j888。Morteza似乎在说Code-First中的关系跨度与EF6不同。不确定... - Sean Kearon
@Sean Kearon:我的回答现在有一个编辑。(我本来只想写一个简短的评论,但它变得越来越长了。) - Slauma
Slauma,感谢您回复并提供有关行为的如此全面的答案。 - Sean Kearon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接