Entity framework EF 4.1加载筛选的子集合对多对多不起作用
我一直在看,但无法让它在多对多关系中发挥作用 我创建了一个简单的模型: 简要说明:Entity framework EF 4.1加载筛选的子集合对多对多不起作用,entity-framework,entity-framework-4.1,Entity Framework,Entity Framework 4.1,我一直在看,但无法让它在多对多关系中发挥作用 我创建了一个简单的模型: 简要说明: 一个学生可以修很多课程,课程可以有很多学生 一名学生可以做许多演示,但是演示只能由一名学生做 因此,我们拥有的是学生和课程之间的多对多关系,以及学生和演示之间的一对多关系 我还添加了一个学生、一个课程、一个演示 以下是我正在运行的代码: class Program { static void Main() { using (var context = new SportsModel
一个
学生
可以修很多课程
,课程
可以有很多学生
一名
学生
可以做许多演示
,但是演示
只能由一名学生
做因此,我们拥有的是
学生
和课程
之间的多对多关系,以及学生
和演示
之间的一对多关系
我还添加了一个学生
、一个课程
、一个演示
以下是我正在运行的代码:
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();
}
}
}
加载演示文稿后,我看到演示文稿的计数从0变为1:
但是,在对课程执行相同操作后,没有任何变化:
因此,我尝试在不调用查询的情况下加载课程,结果正如预期的那样:
(我删除了Where
子句以进一步强调这一点-最后两次加载尝试的不同之处仅在于“Query()”调用)
现在,我看到的唯一区别是,一种关系是一对多,而另一种关系是多对多。这是EF错误,还是我遗漏了什么
顺便说一句,我检查了最近两次
课程
加载尝试的SQL调用,它们是100%相同的,因此似乎是EF未能填充集合。我可以准确地重现您描述的行为。我的工作是:
context.Entry(student)
.Collection(s => s.Courses)
.Query()
.Include(c => c.Students)
.Where(c => c.Id == 1)
.Load();
我不知道当我们只想加载一个集合时,为什么还要强制加载多对多关系的另一端(Include(…)
)。对我来说,这感觉确实像一个bug,除非我错过了这个需求的一些隐藏的原因,不管是否有文档记录
编辑
另一个结果:原始查询(不包括)
。。。实际将课程加载到DbContext
中,作为
var localCollection = context.Courses.Local;
。。。显示。Id为1的课程确实在这个集合中,这意味着:加载到上下文中。但它不在student对象的子集合中
编辑2
也许这不是一个bug
首先:我们在这里使用两个不同版本的Load
:
DbCollectionEntry<TEntity, TElement>.Load()
。。。智能感知说:
枚举查询,以便
服务器查询,例如
System.Data.Entity.DbSet,
System.Data.Objects.ObjectSet,
System.Data.Objects.ObjectQuery,
和其他人的查询结果
将加载到关联的
System.Data.Entity.DbContext,
System.Data.Objects.ObjectContext或
客户端上的其他缓存。这是
相当于调用ToList,然后
不经批准就把名单扔掉
实际创建
名单
因此,在此版本中,不保证填充子集合,只保证将对象加载到上下文中
问题是:为什么要填充演示文稿
集合,而不是课程
集合。我认为答案是:因为关系Span
关系范围是EF中的一个功能,它自动修复上下文中或刚刚加载到上下文中的对象之间的关系。但并非所有类型的关系都是如此。仅当一端的重数为0或1时才会发生
在我们的示例中,这意味着:当我们将演示文稿
加载到上下文中时(通过我们过滤的显式查询),EF还将演示文稿
实体的外键加载到学生
实体-“透明”,这意味着,无论FK是否在not模型中作为属性公开。此加载的FK允许EF识别加载的演示文稿
属于已在上下文中的学生
实体
但课程的情况并非如此。课程没有学生
实体的外键。中间有多对多联接表。因此,当我们加载课程
时,EF不会识别这些课程属于上下文中的学生
,因此不会修复学生
实体中的导航集合
出于性能原因,EF仅对引用(而非集合)执行此自动修复:
要修复关系,请透明地执行EF
重写查询以使
所有关系的关系信息
其上的多重数为0..1或1
另一端;换句话说
是实体的导航属性
参考资料。如果一个实体
与多重性的关系
大于1时,EF将不会恢复
关系信息,因为它可以
性能受到影响,与之相比
带着一个外国人
记录的其余部分。带来
关系信息意味着检索所有
记录中包含的外键
引自第128页,共页
它基于EF4和ObjectContext,但我认为这在EF4.1中仍然有效,因为DbContext主要是ObjectContext的包装器
不幸的是,在使用Load
时要记住相当复杂的东西
和另一次编辑
那么,当我们想要明确地加载多对多关系的一个过滤面时,我们能做什么呢?也许只有这样:
student.Courses = context.Entry(student)
.Collection(s => s.Courses)
.Query()
.Where(c => c.Id == 1)
.ToList();
是的,有趣的发现。这会产生更多的SQL代码——基本上是为具有所有联接的学生进行新的选择(而没有Include
的版本只使用Courses
和映射表来获取必要的数据)。s
DbExtensions.Load(this IQueryable source);
student.Courses = context.Entry(student)
.Collection(s => s.Courses)
.Query()
.Where(c => c.Id == 1)
.ToList();