Yak*_*ych 13 entity-framework entity-framework-4.1
我一直在寻找在显式加载相关实体时应用过滤器,并且无法使其适用于多对多关系.
我创建了一个简单的模型:
简要说明:
A Student可以占用很多Courses,Course也可以有很多Students.
A Student可以制作很多Presentation,但Presentation只能由一个制作Student.
所以我们所拥有的是Students和Courses之间的多对多关系,Student以及和之间的一对多关系Presentations.
我还添加了一个Student,一个Course和一个Presentation相互关联.
这是我正在运行的代码:
class Program
{
static void Main()
{
using (var context = new SportsModelContainer())
{
context.Configuration.LazyLoadingEnabled = false;
context.Configuration.ProxyCreationEnabled = false;
Student student = context.Students.Find(1);
context.
Entry(student).
Collection(s => s.Presentations).
Query().
Where(p => p.Id == 1).
Load();
context.
Entry(student).
Collection(s => s.Courses).
Query().
Where(c => c.Id == 1).
Load();
// Trying to run Load without calling Query() first
context.Entry(student).Collection(s => s.Courses).Load();
}
}
}
Run Code Online (Sandbox Code Playgroud)
加载演示文稿后,我看到计数Presentations从0更改为1:
.但是,在做同样的Courses事情后没有任何改变: 
所以我尝试在没有调用的情况下加载课程Query,它按预期工作: 
(我删除了该Where子句以进一步强调这一点 - 最后两次加载尝试仅因"Query()"调用而不同)
现在,我看到的唯一区别是一个关系是一对多,而另一个关系是多对多.这是EF的错误,还是我错过了什么?
顺便说一句,我检查了最后两次Course加载尝试的SQL调用,它们是100%完全相同的,所以它似乎是EF无法填充集合.
Sla*_*uma 18
我可以准确再现您描述的行为.我的工作是这样的:
context.Entry(student)
.Collection(s => s.Courses)
.Query()
.Include(c => c.Students)
.Where(c => c.Id == 1)
.Load();
Run Code Online (Sandbox Code Playgroud)
我不知道为什么Include(...)当我们只想加载一个集合时,我们也应该被迫加载多对多关系()的另一面.对我来说,它确实像一个错误,除非我错过了这个要求的隐藏原因,这个原因记录在某处或没有.
编辑
另一个结果:您的原始查询(不包括)...
context.Entry(student)
.Collection(s => s.Courses)
.Query()
.Where(c => c.Id == 1)
.Load();
Run Code Online (Sandbox Code Playgroud)
......实际上将课程加载到DbContext......
var localCollection = context.Courses.Local;
Run Code Online (Sandbox Code Playgroud)
......表明.Id 1的课程确实在这个集合中,这意味着:加载到上下文中.但它不在学生对象的子集合中.
编辑2
也许这不是一个错误.
首先:我们在这里使用两个不同的版本Load:
DbCollectionEntry<TEntity, TElement>.Load()
Run Code Online (Sandbox Code Playgroud)
Intellisense说:
从数据库加载实体集合.请注意,上下文中已存在的实体不会被数据库中的值覆盖.
对于其他版本(扩展方法IQueryable)...
DbExtensions.Load(this IQueryable source);
Run Code Online (Sandbox Code Playgroud)
... Intellisense说:
枚举查询,以便对于服务器查询(如System.Data.Entity.DbSet,System.Data.Objects.ObjectSet,System.Data.Objects.ObjectQuery等)的查询,查询结果将加载到关联的系统中.Data.Entity.DbContext,System.Data.Objects.ObjectContext或客户端上的其他缓存.这相当于调用ToList然后丢弃列表而没有实际创建列表的开销.
因此,在此版本中,不保证填充子集合,只保证将对象加载到上下文中.
问题仍然存在:为什么要Presentations填充集合而不是Courses集合.我认为答案是:因为关系跨度.
关系Span是EF中的一项功能,它自动修复上下文中或刚刚加载到上下文中的对象之间的关系.但是对于所有类型的关系都不会发生这种情况.只有当一端的多重性为0或1时才会发生.
在我们的示例中,它表示:当我们加载Presentations到上下文中时(通过我们过滤的显式查询),EF还会将Presentationentites 的外键加载到Student实体 - "透明地",这意味着,无论FK是否作为属性公开在模型中没有.这个加载的FK允许EF识别加载的Presentations属于Student已经在上下文中的实体.
但这不是Courses收集的情况.课程没有Student实体的外键.中间有多对多的连接表.因此,当我们加载CoursesEF时,不会识别这些课程属于Student上下文中的那些课程,因此不会修复Student实体中的导航集合.
由于性能原因,EF仅为引用(而不是集合)执行此自动修复:
为了修复关系,EF透明地重写查询,为所有在另一端具有0..1或1的多重性的关系带来关系信息; 换言之,作为实体引用的导航属性.如果一个实体与多重性大于1的关系,EF将不会带回关系信息,因为它可能会受到性能影响,并且与将一个外国人与其余记录一起带来相比.带来关系信息意味着检索记录具有的所有外键.
从Zeeshan Hirani的第128页引用EF的深度指南.
它基于EF 4和ObjectContext,但我认为这在EF 4.1中仍然有效,因为DbContext主要是ObjectContext的包装器.
不幸的是,使用时要记住相当复杂的东西Load.
另一个编辑
那么,当我们想要明确地加载多对多关系的一个过滤方时,我们能做什么呢?也许只有这样:
student.Courses = context.Entry(student)
.Collection(s => s.Courses)
.Query()
.Where(c => c.Id == 1)
.ToList();
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4647 次 |
| 最近记录: |