c# - 为什么 Entity Framework Core 急于加载不包含的相关实体

标签 c# asp.net-core json.net ef-core-2.1

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

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

员工表

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

事件表

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

事件责任表

+----+-------------+----------+
| 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; }

事件_职责模型

多对多链接表模型。

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; }

我在 Controller 方法中运行了以下 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": []
  }
]

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

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

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

所以我的问题是:为什么即使我没有加载/包含 Creator 导航属性也会包含它?另外,我也想知道如何 不是 包含相关的 Creator 数据并仅在 JSON 中显示 Event Responsibilities 数据。

最佳答案

如果您查看 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.Creator 是不同于 EeventsResponsibilities.EmployeeEmployee,您就不会遇到这种异常情况。

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

作为快速解决方法,您可以简单地在 Creator 属性上添加 JsonIgnoreAttribute:

[JsonIgnore]
public Employee Creator { get; set; }

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

关于c# - 为什么 Entity Framework Core 急于加载不包含的相关实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54107746/

相关文章:

c# - 如何在单元测试中模拟 DateTime.Now?

c# - 如何读取子项目中appsettings文件的内容

c# - 使用 Newtonsoft 简化对象的反序列化

c# - 如何使用 EF Core 将 JSON 存储在实体字段中?

c# - 以编程方式设置浏览器 cookie (Firefox)

c# - 为什么 Microsoft 建议不要使用具有可变值的只读字段?

c# - 清净属性(property)申报

asp.net-mvc - 在其他项目的 mvc 中使用部分

asp.net-core - NPoco 在 .net Core 中的使用

c# - 解析 JToken 得到 Key Value 对