是否可以手动将现有对象分配给 Entity Framework (数据库优先)对象的导航属性?
这个问题的上下文是我在尝试带回一个(经过严格过滤的)对象列表时遇到了问题,其中包含所有子项和后代,以便在处理上下文后可以在内存中使用完整的图形。
我尝试通过 .Include()
语句执行此操作,使用如下内容:
using (var ctx = new MyEntities())
{
myParents = ctx.Parents
.Where(p => MyFilter(p))
.Include(p => p.Children)
.Include(p => p.Children.Select(c=>c.Grandchildren))
.Include(p => p.Children.Select(c=>c.Grandchildren.Select(g=>g.GreatGrandChildren)));
}
但是生成的查询运行速度太慢,因为使用嵌套 include 语句存在已知的性能问题(如许多地方所述,包括 this blog)。
我可以在没有性能问题的情况下撤回 parent 、子女和孙子女 - 我只有在为曾孙子女包含最后一个 .Include()
语句时才会遇到麻烦。
通过从已经检索到的 GrandChildren 构建 GrandChildrenIds 列表并执行类似操作,我可以通过第二个单独的查询轻松地从数据库中取回 GreatGrandChildren 对象:
greatGrandKids = ctx.GreatGrandChildren.Where(g=>ids.Contains(g.GrandChildId)).ToList();
但是现在,一旦我处理了上下文,我就无法在不触发对象上下文处理异常的情况下执行类似 grandChildA.GreatGrandChildren
的操作。
我可能有多达几千个 GrandChildren 对象,所以我真的想避免往返数据库以获取每个对象的 GreatGrandChildren,这排除了简单地使用 .Load()
每个 GrandChild 对象,对吧?
我可以通过在后续代码中每次需要时从 greatGrandKids
中查找所需的曾孙,或者甚至通过添加新的(未映射的)属性,例如 .GreatGrandChildrenLocal
到 GrandChild
类并预先分配它们,但这些都感觉非常笨拙和丑陋。我更愿意找到一种方法来访问每个 GrandChild
对象上的现有 .GreatGrandChildren
导航属性。
尝试使用类似这样的方式分配给导航属性:
grandchild.GreatGrandChildren = greatGrandKids
.Where(g=>g.GrandChildId == grandChild.Id)
.ToList();
当我随后尝试访问 grandchild.GreatGrandChildren
时也失败了(仍然给出对象处置异常)。
所以我的问题是:
有没有一种方法可以将我已经从数据库中检索到的现有 GreatGrandChdildren 对象分配给 GrandChild 对象的 .GreatGrandChdildren
导航属性,使它们可用(仅读取操作需要)上下文被释放后?
(或者确实有解决问题的不同方法?)
最佳答案
如果您禁用代理创建:
ctx.Configuration.ProxyCreationEnabled = false;
然后从/向导航属性读取和写入完全按照预期工作,而无需尝试延迟加载实体并抛出对象处置异常。
所以我们有类似的东西:
using (var ctx = new MyEntities())
{
myParents = ctx.Parents
.Where(p => MyFilter(p))
.Include(p => p.Children)
.Include(p => p.Children.Select(c=>c.Grandchildren));
//skip the final GreatGrandChildren include statement
//get the associated grandchildren & their ids:
var grandKids = myParents.SelectMany(p=>p.Children)
.SelectMany(c=>c.Grandchildren)
.ToList();
var ids = grandKids.Select(g=>g.Id)).ToList();
//Get the great grandkids:
var greatGrandKids = ctx.GreatGrandChildren
.Where(g=>ids.Contains(g.GrandChildId)).ToList();
//Assign the greatgrandchildren to the grandchildren:
foreach (grandChild in grandKids)
{
grandChild.GreatGrandChildren = greatGrandKids
.Where(g=>g.GrandChildId == grandChild.Id)
.ToList();
}
}
现在我们可以在上下文之外访问 .GreatGrandChildren
属性,而不会触发上下文处理的异常。虽然这仍然感觉有点困惑,但它比使用原始 Include()
语句或在每个 GrandChild 上调用 .Load()
便宜得多。
注意由于这些对象仅用于读取操作并且我不需要延迟加载,因此在我的情况下关闭代理创建没有负面影响。如果写入操作和/或延迟加载也是必需的,那么我们还需要考虑为给定的 EF 上下文关闭此功能的影响。
关于c# - 手动将现有对象分配给 Entity Framework 导航属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34528958/