c# - 手动将现有对象分配给 Entity Framework 导航属性

标签 c# entity-framework

是否可以手动将现有对象分配给 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 中查找所需的曾孙,或者甚至通过添加新的(未映射的)属性,例如 .GreatGrandChildrenLocalGrandChild 类并预先分配它们,但这些都感觉非常笨拙和丑陋。我更愿意找到一种方法来访问每个 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/

相关文章:

c# - TCP设置时间通常这么慢(1秒)吗?

c# - 对选择合适的 ORM 感到困惑?

c# - Visual Studio 中的 Entity Framework : database is not created with DbContext base constructor

c# - using block 会关闭数据库连接吗?

c# - 如何反序列化包含多维数组的json对象?

c# - WPF ObservableCollection 在创建新的 ObservableCollection 时不更新 DataGrid

c# - 同步更新调用期间乐观并发异常

c# - 仅替换 HTML 标记中的引号的正则表达式

c# - MVC 绑定(bind)产品到订单

c# - Entity Framework 5 : Entity is lazy loaded but the navigation properties are null when observing the Local collection