EF Core Eager Loading 嵌套集合

Posted

技术标签:

【中文标题】EF Core Eager Loading 嵌套集合【英文标题】:EF Core Eager Loading nested collections 【发布时间】:2019-04-27 22:15:55 【问题描述】:

我正在尝试在 Entity Framework Core 中加载一个相关的模式,但由于某种原因,当我没有在我的 Include() 调用中请求它时,会加载一个嵌套集合。

这是我的两个模型 -

Driver.cs

public partial class Driver : IBaseEntity

    public short DriverId  get; set; 
    public string Surname  get; set; 
    public string Initials  get; set; 
    public byte DriverStatusTypeId  get; set; 

    public DriverStatusType DriverStatusType  get; set; 

DriverStatusType.cs

public partial class DriverStatusType

    public DriverStatusType()
    
        Drivers = new HashSet<Driver>();
    

    public byte DriverStatusTypeId  get; set; 
    public string DriverStatusTypeName  get; set; 
    public string Description  get; set; 

    public ICollection<Driver> Drivers  get; set; 

DriversService.cs

public class DriverService : IDriverService

    public DriverService(MyContext context)
    
        Context = context;
    

    public MyContext Context  get; 

    public async Task<IEnumerable<Driver>> GetAllDrivers()
    
        var drivers = await Context
            .Drivers
            .Include(d => d.DriverStatusType)
            .toListAsync();

        return drivers;
    

    public async Task<Driver> GetDriverById(int id)
    
        var driver = await Context
            .Drivers
            .Include(d => d.DriverStatusType)
            .Where(d => d.DriverId == id)
            .FirstOrDefaultAsync();

        return driver;
    

现在,当我从控制器调用 GetDriverById(int id) 方法时,我得到了预期的结果 -


    "driverId": 1,
    "surname": "Stark",
    "initials": "T",
    "driverStatusTypeId": 2,
    "driverStatusType": 
        "driverStatusTypeId": 2,
        "driverStatusTypeName": "Available",
        "description": "This driver is available",
        "drivers": []
    

但是 GetAllDrivers() 方法返回嵌套的 drivers 集合,这意味着我要返回的数据是巨大的 -

[
    
        "driverId": 1,
        "surname": "Stark",
        "initials": "T",
        "displayText": "Tony Stark",
        "driverStatusTypeId": 2,
        "driverStatusType": 
            "driverStatusTypeId": 2,
            "driverStatusTypeName": "Available",
            "description": "This driver is available",
            "drivers": [
                
                    "driverId": 2,
                    "surname": "Rogers",
                    "initials": "S",
                    "driverStatusTypeId": 2
                ,
                
                    "driverId": 3,
                    "surname": "Romanoff",
                    "initials": "N",
                    "driverStatusTypeId": 2
                ,
                
                    "driverId": 4,
                    "surname": "Banner",
                    "initials": "B",
                    "driverStatusTypeId": 2
                ,
                ...

我认为预加载的想法是只包含您在 include 语句中指定的相关模型,但似乎情况并非如此。有人可以解释这里发生了什么吗?

【问题讨论】:

drivers[] 是否已经在上一次调用的内存中?您的 DbContext 的范围是什么? EF为GetAllDrivers()生成的SQL查询是什么 【参考方案1】:

我认为预加载的想法是只包含您在 include 语句中指定的相关模型,但似乎情况并非如此。有人可以解释这里发生了什么吗?

你是对的,事实并非如此。急切加载的想法是确保您指定的相关数据被加载。这并不意味着/保证不会包含相关数据

EF Core 文档的Loading Related Data 部分对此进行了部分解释:

提示

Entity Framework Core 将自动修复先前加载到上下文实例中的任何其他实体的导航属性。因此,即使您没有明确包含导航属性的数据,如果之前加载了部分或所有相关实体,该属性仍可能会被填充。

导航属性修复意味着任何时候实体实例被具体化,所有相关的导航属性都会更新以反映它,例如Driver被添加到Driver.DriverStatusType.Drivers,反之亦然。

请注意,在使用跟踪查询时,这可能发生在非包含查询具体化 (ToList()) 之后,因为更改跟踪器会跟踪对象引用并在您执行其他跟踪查询时自动更新它们.

该修复过程的另一个效果是,当您包含关系的一端时,会自动填充另一端的反向导航属性。

因此,即使在第一种情况下,Drivers 属性也应该被填充并包含单个项目。这就是我的干净测试中实际发生的情况,不知道为什么你会有所不同 - 可能是序列化程序隐藏了它?

无论如何,这意味着您无法真正控制导航属性的内容。您可以准确控制要返回的内容的唯一方法是使用特殊的 DTO/ViewModel 等类和投影 (Select)。

【讨论】:

以上是关于EF Core Eager Loading 嵌套集合的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework Core Eager Loading 然后包含在集合中

EF Core Eager 加载返回 null

rails / 检查 eager_loading

ruby 包括,预加载,eager_loading

Laravel - Eager Loading BelongsToMany 关系

Laravel Eager Loading - 仅加载特定列