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

C# 如何使EF Core Include()不跟踪实体?,c#,entity-framework,.net-core,entity-framework-core,C#,Entity Framework,.net Core,Entity Framework Core,TLDR:我想知道是否可以在EF core中对单个查询中的一种实体类型使用不同的“包含逻辑” 编辑:为了清楚起见,我在标题中说了跟踪实体,因为我认为EF就是这样做的,但是。在任何人建议之前,AsNoTracking()在这里没有任何用处 目前的问题在于一个相对较小的React应用程序,该应用程序由ASP.NET核心web api应用程序支持。我想要完成的是,当我调用api/parents时,我希望应用程序给我一个json,如下所示: [ { "id": "parentid1",

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

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

目前的问题在于一个相对较小的React应用程序,该应用程序由ASP.NET核心web api应用程序支持。我想要完成的是,当我调用
api/parents
时,我希望应用程序给我一个json,如下所示:

[
  {
    "id": "parentid1",
    "extraProperty": "value",
    "children": [
      {
        "id": "childid1",
        "parent": {
          "id": "parentid1",
          "extraProperty": "value"
        }
      }
    ]
  }
]
[
  {
    "id": "parentid1",
    "extraProperty": "value",
    "children": [
      {
        "id": "childid1",
        "parent": {
          "id": "parentid1",
          "extraProperty": "value",
          "children": [
            {
              "id": "childid1",
              "parent": {
                "id": "parentid1",
                "extraProperty": "value"
                ...
              }
            }
          ]
        }
      }
    ]
  }
]
我的设置如下所示:

[
  {
    "id": "parentid1",
    "extraProperty": "value",
    "children": [
      {
        "id": "childid1",
        "parent": {
          "id": "parentid1",
          "extraProperty": "value"
        }
      }
    ]
  }
]
[
  {
    "id": "parentid1",
    "extraProperty": "value",
    "children": [
      {
        "id": "childid1",
        "parent": {
          "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();
在这之后,我有自动映射实体到dto,那里没有太多的事情发生。我还为应用程序使用默认的Json序列化程序(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"
        }
      }
    ]
  }
]
[
  {
    "id": "parentid1",
    "extraProperty": "value",
    "children": [
      {
        "id": "childid1",
        "parent": {
          "id": "parentid1",
          "extraProperty": "value",
          "children": [
            {
              "id": "childid1",
              "parent": {
                "id": "parentid1",
                "extraProperty": "value"
                ...
              }
            }
          ]
        }
      }
    ]
  }
]
如果您查看我的Include调用,它明确地表示,对于我的子对象,我只希望父对象,而不希望在父对象内有其他引用

据我猜测,EF在遇到该查询的任何父对象时,都会使用初始设置
.Include(child)。然后Include(parent)
。因此,当它在
子对象中找到一个
父对象时,它使用
.Include(子对象)。然后Include(父对象)
,而不是使用no Include(在
然后Include
之后我没有任何包含)

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

调用
然后包含(父)
是多余的,因为您从父级开始-具体化的子级将已经填充了其
父级
属性。您需要自定义序列化,如@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;
        } );

我决定在事情的前端解决这个问题,因为其他的解决方案根本不适合我。为了将来的参考,我将发布我的旅程:

EF

起初,在数据访问层做这个把戏似乎是合乎逻辑的,因为我只是错过了一件非常基本的事情。我意识到在评论之后这样做是不合逻辑的。基本上,当您从EF获取实体并且您的对象具有引用时,每次您有一个对象不止一次,它们都是相同的对象(通过引用,而不是值)。因此,期望它们包含不同的数据(在我的例子中,一个有一些细节,另一个没有)是不符合逻辑的

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

目前,我正在React应用程序上手动执行任务。当我从服务器获取数据时,我只需要

parent.children.forEach(c => c.parent = parent);
请记住,通常情况下
parent.children.parent
null
。实体对象或DTO不是这样,只是序列化后的情况。因为我为序列化程序设置了
ReferenceLoopHandling.Ignore

这修复了应用程序的所有问题,只是api似乎不完整。返回的JSON看起来缺少了什么(可能只是我,IDK)

最后,我考虑在不同的场景中使用多个相同对象类型的DTO,或者在api中返回id字段,以确保长期的完整性


感谢大家的评论和回复。

这是JSON序列化/AM映射/dto问题,而不是EF核心查询问题。请注意,
child.Parent
对象与
Parent
引用相同,其中
child
位于
Parent.Children
集合中。@IvanStoev因此EF将始终给我一个Parent对象实例,并在整个查询过程中保持它,否则的操作似乎不符合逻辑,对吗?如果是这样,我可能会尝试映射选项并更新问题/答案。这是默认的关系修复逻辑,它根据FK值设置与上下文图中其他对象相关的所有对象的导航属性。即使您没有跟踪实体,物化对象看起来也一样,因为急切加载处理和跟踪/无跟踪查询几乎没有关联。