当实体框架为与多对多关系表连接的两个数据库表(比如Table1和Table2)生成ObjectContext时,它不会为外部参照表创建对象,而是选择关系两端的集合属性。所以在表1上有EntityCollection<Table2> Table2s
,在表2上有EntityCollection<Table2> Table1s
。在大多数情况下,这实际上非常棒。。。
但是,在这个场景中,我有一个整数列表,这些整数表示应该在Table1.Table2s集合中的Table2行的数据库ID。
我看不出有任何方法可以使用实体键来设置该集合,所以我一直在ObjectContext中选择这些键,这已经是一项毫无理由的大量工作了。我希望LINQ to Entities能够智能地推迟执行,并像我希望的那样在SQL服务器上执行所有操作(尽管我的Where使用Contains,它可能会也可能不会正确地转换为SQL中的IN())。所以我可以走到:
table1instance.Table2s.Clear();
var table2sToInclude = context.Table2s.Where(
t =>
listOfTable2DatabaseIds.Contains(t.Id));
但是没有EntityCollection<T>.AddRange(IEnumerable<T>)
或任何东西,当然也没有IEnumerable<T>.ToEntityCollection<T>()
扩展方法,所以我不知道在这一点上该如何处理这些结果。我所能做的就是
foreach (var table2 in table2sToInclude)
{
table1instance.Table2s.Add(table2);
}
这看起来很荒谬,我知道这会迫使很多不必要的评估。
有没有一种"正确"的方法,或者说,"不那么蹩脚"的方法来做到这一点?
没有EF不会推迟任何查询执行。没有什么能比得上select中的insert。Linq-to实体只是查询语言,查询的职责是执行。它与EF本身提供的持久性功能是严格分离的。
如果你想在表1中的现有项目和表2中的现有项目之间创建关系,你可以使用这样的代码:
using (var ctx = new YourContext())
{
var table1 = new Table1 { Id = 123 };
ctx.Table1s.Attach(table1);
foreach (var table2 in table2sToInclude.Select(id => new Table2 { Id = id }))
{
ctx.Table2s.Attach(table2);
order.Table2s.Add(table2);
}
ctx.SaveChanges();
}
此代码创建id为123的表1的项和table2sToInclude中表2的所有项之间的关系,而不从数据库加载任何单个记录。
是什么让一个接一个地添加记录变得"蹩脚"?你知道AddRange
的好处是什么吗?典型集合中的AddRange
扩展了内部数组的容量,只将项复制到扩展数组中。EntityCollection
不是典型的数组,它必须处理每个添加的实体。因此,即使会有一些AddRange
,它也会在内部迭代项目并逐个处理它们。