如何使EF Core Include()不跟踪实体?

问题描述 投票:0回答:2

TLDR:我想知道在EF核心中是否可以在单个查询中为一个实体类型使用不同的“包含逻辑”。

编辑:为了清楚,在标题中我说跟踪实体,因为我认为这是EF的作用,但.AsNoTracking()在这里没有任何用处,在任何人建议之前。

手头的问题是在一个相对较小的React应用程序上,该应用程序支持ASP.NET Core web api应用程序。我想要完成的是,当我调用api/parents时,我希望应用程序给我一个看起来像这样的json:

[
  {
    "id": "parentid1",
    "extraProperty": "value",
    "children": [
      {
        "id": "childid1",
        "parent": {
          "id": "parentid1",
          "extraProperty": "value"
        }
      }
    ]
  }
]

我的设置如下:

EF查询:

(from p in _context.Parents
 select p)
 .Include(p => p.Children)
     .ThenInclude(c => c.Parent)
 .ToList();

之后,我将AutoMapper映射到dto的实体,那里没有太多进展。我还使用默认的Json Serializer(Newtonsoft)作为应用程序。它配置有SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore

通过此设置,api调用将返回此响应:

[
  {
    "id": "parentid1",
    "extraProperty": "value",
    "children": [
      {
        "id": "childid1"
      }
    ]
  }
]

你可以看到“忽略”父母的自我引用。

我想出的解决方案是,我应该将Newtonsoft配置为“序列化”参考循环,然后我尝试了。并抛出,因为上面提供的EF查询返回一个如下所示的实体列表:

[
  {
    "id": "parentid1",
    "extraProperty": "value",
    "children": [
      {
        "id": "childid1",
        "parent": {
          "id": "parentid1",
          "extraProperty": "value",
          "children": [
            {
              "id": "childid1",
              "parent": {
                "id": "parentid1",
                "extraProperty": "value"
                ...
              }
            }
          ]
        }
      }
    ]
  }
]

如果你查看我的Include调用,它会明确地说明对于我的子对象,我只想要父对象而在该父对象中没有其他引用。

据我所知,EF在遇到该查询的任何Parent对象时使用初始设置.Include(child).ThenInclude(parent)。因此,当它在Parent对象中找到Child对象时,而不是使用no includes(我在ThenInclude之后没有任何包含),它使用.Include(child).ThenInclude(parent)

我不想通过使用映射器或序列化器来解决这个问题,如果我不需要的话。我想知道我所寻求的是否可能:在单个查询中为一个实体类型使用不同的“包含逻辑”。

c# entity-framework .net-core entity-framework-core
2个回答
1
投票

ThenInclude(parent)调用是多余的,因为你从父母开始 - 物化的孩子已经填充了他们的Parent属性。您需要将序列化自定义为@IvanStoev在第一条评论中说明。

另一种方法是按父级查询子项,然后投影所需的结果:

var parents = _context.Children
    .Include( c => c.Parent )
    .GroupBy( c => c.Parent )
    .ToArray()
    .Select( g => 
        {
            // assign children to g.Key (the parent object)
            g.Key.Children = g.ToArray(); // I don't know type of `Children` property
            // select parent
            return g.Key;
        } );

0
投票

我决定在前端解决问题因为其他解决方案对我不起作用。为了将来的参考,我将发布我的旅程:

EF

起初,在数据访问层执行技巧似乎是合乎逻辑的,因为我只是错过了一个非常基本的东西。我意识到在评论之后这样做是不合逻辑的。基本上,当您从EF获取实体并且您的对象具有引用时,每次您有一个对象不止一次时,它们都是相同的对象(通过引用而不是值)。所以期望他们在其中有不同的数据(在我的情况下,其中一个有一些细节,另一个没有)是不合逻辑的。

在那之后,我想也许我可以在绘图阶段解决这个问题。我尝试为不同的场景做不同的配置文件/配置,它变得非常难看。然后我想可能使用一个Profile并做一些AfterMap()逻辑可以工作(最初加载所有数据,然后剥离不需要的数据)。但是,除非你做一些丑陋的东西,否则同样的原则也适用。 AutoMapper还保留引用,因此当您修改child.Parent对象时,原始Parent也会被修改。我本来可以使用克隆,也许还有其他一些技巧,但就像我说的那样,在我看来它变得丑陋。

现在我在React应用程序上手动完成任务。当我从服务器获取数据时,我就这样做了

parent.children.forEach(c => c.parent = parent);

请记住,通常parent.children.parentnull。实体对象或DTO不是这种情况,它是序列化后的情况。因为我为序列化器设置了ReferenceLoopHandling.Ignore

这解决了我的应用程序的所有问题,除了我的api似乎不完整。返回的JSON看起来像是缺少一些东西(也许只是我,IDK)。

在一天结束时,我正在考虑为不同的场景设置相同对象类型的多个DTO,或者为了完整性而在api中返回id字段。

感谢所有评论和回复。

© www.soinside.com 2019 - 2024. All rights reserved.