为什么实体框架核心预先加载未包含的相关实体



我正在创建一个ASP。NET核心Web API,并且在查询上下文时遇到问题。

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

员工表

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

我在控制器方法中运行了以下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中第二个返回的Event("Id":2),即使没有EventResponsibility,也会返回两个Creator行。我不认为这是一个懒惰的加载问题,因为导航属性不是虚拟的。

我本来希望Creator对象是null或空的,因为我没有包括它

所以我的问题是:为什么即使我没有加载/包含Creator导航属性,它也会被包含?此外,我还想知道如何不是包括相关的创建者数据,并且只在JSON中显示事件责任数据

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

实体框架核心将自动修复先前加载到上下文实例中的任何其他实体的导航属性。因此,即使您没有明确包含导航属性的数据,如果以前加载了一些或所有相关实体,则该属性仍可能被填充。

通过进行

.ThenInclude(e => e.Employee);

您请求EF获取这个Employee,从而使它在被引用的任何地方都被急切地加载。因为在这种特定情况下,您的Event.Creator与您明确请求的EventResponsibilities.Employee相同,所以在它被提取到EventResponsibilities中之后,它也被提取到您的Event中。如果你的Event.Creator是不同于EeventsResponsibilities.EmployeeEmployee,你就不会经历这种异常。

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

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

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

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

相关内容

  • 没有找到相关文章

最新更新