为什么 Entity Framework 核心急切加载未包含的相关实体

Why is Entity Framework Core eager loading Related Entities that aren't Included

我正在努力创建一个 ASP.NET 核心 Web API,但在查询上下文时遇到了问题。

Web API 通过 3 table 连接到数据库:员工、事件和事件责任。一个员工可以创建多个事件。此外,许多员工可以在每个事件中分配许多事件的责任(因此链接事件责任 table)。

员工Table

+----+-----------------+
| Id |      Name       |
+----+-----------------+
|  1 | First Employee  |
|  2 | Second Employee |
+----+-----------------+

事件Table

+----+------------+---------------+
| Id | Creator_Id |  Event_Name   |
+----+------------+---------------+
|  1 |          1 | Random Event  |
|  2 |          1 | Another Event |
+----+------------+---------------+

活动责任Table

+----+-------------+----------+
| Id | Employee_Id | Event_Id |
+----+-------------+----------+
|  1 |           1 |        1 |
+----+-------------+----------+

其中的型号包括...

事件模型

一个活动只能有一名员工作为创建者。 一个活动也可以有许多员工,他们承担不同的职责。

public int Id { get; set; }
public int? CreatorId { get; set; }
public string EventName { get; set; }

public Employee Creator { get; set; }
public ICollection<EventResponsibilities> EventResponsibilities { get; set; }

员工模型

一个员工可以是许多事件的创造者。 一名员工可以在许多事件中承担责任。

public int Id { get; set; }
public string Name { get; set; }

public ICollection<Event> Event { get; set; }
public ICollection<EventResponsibilities> EventResponsibilities { get; set; }

Event_Responsibilities型号

多对多链接 table 模型。

public int Id { get; set; }
public int? EmployeeId { get; set; }
public int? EventId { get; set; }

public Employee Employee { get; set; }
public Event Event { get; set; }

我在控制器方法中有 运行 以下 LINQ 查询...

return _context.Event.Include(e => e.EventResponsibilities).ThenInclude(e => e.Employee);

期望得到一个JSON,其中包含事件的详细信息、其相应的事件职责以及该职责的相关员工。

我实际得到的是下面的json...

[
  {
    "Id": 1,
    "CreatorId": 1,
    "EventName": "Random Event",
    "Creator": {
      "Id": 1,
      "Name": "First Employee",
      "Event": [],
      "EventResponsibilities": [
        {
          "Id": 1,
          "EmployeeId": 1,
          "EventId": 1
        }
      ]
    },
    "EventResponsibilities": [
      {
        "Id": 1,
        "EmployeeId": 1,
        "EventId": 1,
        "Employee": {
          "Id": 1,
          "Name": "First Employee",
          "Event": [],
          "EventResponsibilities": []
        }
      }
    ]
  },
  {
    "Id": 2,
    "CreatorId": 1,
    "EventName": "Another Event",
    "Creator": {
      "Id": 1,
      "Name": "First Employee",
      "Event": [
        {
          "Id": 1,
          "CreatorId": 1,
          "EventName": "Random Event",
          "EventResponsibilities": [
            {
              "Id": 1,
              "EmployeeId": 1,
              "EventId": 1
            }
          ]
        }
      ],
      "EventResponsibilities": [
        {
          "Id": 1,
          "EmployeeId": 1,
          "EventId": 1,
          "Event": {
            "Id": 1,
            "CreatorId": 1,
            "EventName": "Random Event",
            "EventResponsibilities": []
          }
        }
      ]
    },
    "EventResponsibilities": []
  }
]

这不是我想要或期望的... 正如您所看到的,出于某种原因,事件模型的创建者导航 属性 正在显示,即使我 not 在查询中请求它,因为我没有使用 Include( ) 为之。

此外,如果您查看 JSON 中返回的第二个事件 ("Id": 2),即使没有 EventResponsibilities,也会返回两个创建者行。我不认为这是一个延迟加载问题,因为导航属性不是虚拟的。

我原以为 Creator 对象为 null 或空,因为我没有包含它。

所以我的问题是:为什么创建者导航 属性 被包括在内,即使我没有 load/Include 它?另外,我也想知道如何 不是 在 JSON.

中包含相关的创建者数据并仅显示事件责任数据

如果你查看 documentation,有一个特定的注释实际上是你掉入的陷阱:

Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.

通过

.ThenInclude(e => e.Employee);

您请求 EF 获取此 Employee 从而导致它在任何被引用的地方被预先加载。因为在这种特定情况下,您的 Event.Creator 与您明确请求的 EventResponsibilities.Employee 相同,所以在它被提取到 EventResponsibilities 之后,它也会被提取到您的 Event 中。 如果您的 Event.CreatorEeventsResponsibilities.Employee 不同 Employee,您就不会遇到这种异常情况。

这种意外行为和冗余数据是人们使用单独的 DTO 从控制器返回数据以确保 API returns 所需数据量恰到好处的原因之一。

作为快速解决方法,您只需在 Creator 属性:

上添加 JsonIgnoreAttribute
[JsonIgnore]
public Employee Creator { get; set; }

但我个人认为将 DTO 和实体混合在同一个 class 中不是一个好主意,因为只要任何实体发生变化,这都会引起各种麻烦。