为什么 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.Creator
与 EeventsResponsibilities.Employee
不同 Employee
,您就不会遇到这种异常情况。
这种意外行为和冗余数据是人们使用单独的 DTO 从控制器返回数据以确保 API returns 所需数据量恰到好处的原因之一。
作为快速解决方法,您只需在 Creator
属性:
上添加 JsonIgnoreAttribute
[JsonIgnore]
public Employee Creator { get; set; }
但我个人认为将 DTO 和实体混合在同一个 class 中不是一个好主意,因为只要任何实体发生变化,这都会引起各种麻烦。
我正在努力创建一个 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.Creator
与 EeventsResponsibilities.Employee
不同 Employee
,您就不会遇到这种异常情况。
这种意外行为和冗余数据是人们使用单独的 DTO 从控制器返回数据以确保 API returns 所需数据量恰到好处的原因之一。
作为快速解决方法,您只需在 Creator
属性:
JsonIgnoreAttribute
[JsonIgnore]
public Employee Creator { get; set; }
但我个人认为将 DTO 和实体混合在同一个 class 中不是一个好主意,因为只要任何实体发生变化,这都会引起各种麻烦。