EF Core查询意外加载关联实体全部属性的问题排查
在基于Entity Framework(EF)和SQL Server搭建ASP.NET Core 7 Web API时,需求是获取AdvertiserConcern实体本身,加上关联Concern实体的ContactId和Name两个属性。
使用以下查询时:
var list = await _dataContext.AdvertiserConcerns .Include(ac => ac.Concern) .Select(ac => new { ac, ac.Concern.ContactId, ac.Concern.Name }) .ToListAsync(cancellationToken);
实际会加载Concern的全部属性;但改用逐个指定AdvertiserConcern属性的查询方式:
var list = await _dataContext.AdvertiserConcerns .Include(ac => ac.Concern) .Select(ac => new { ac.ConcernId, ac.AdvertiserId, ac.StartDate, ac.EndDate, ac.Concern.ContactId, ac.Concern.Name }) .ToListAsync(cancellationToken);
则符合预期,只加载指定的Concern属性。
附AdvertiserConcern实体类代码:
public class AdvertiserConcern : Entity { public required int ConcernId { get;set; } public required int AdvertiserId { get;set; } public required DateTime StartDate { get; set; } public DateTime? EndDate { get; set; } public string? Remark { get; set; } public Client? Concern { get; set; } public Client? Advertiser { get; set; } }
这是EF Core的查询翻译规则导致的,核心差异在于你是否选择了整个AdvertiserConcern实体对象:
当你在
Select中直接包含ac(完整实体)时,EF Core会判定你需要完整的AdvertiserConcern实例,包括其关联的导航属性。即便你同时指定了Concern的两个属性,加上之前显式调用的Include(ac => ac.Concern),EF Core会为了保证实体的完整性,加载Concern的所有属性,而非仅你指定的字段。当你逐个指定
AdvertiserConcern的属性时,你是在构建一个自定义的匿名类型,EF Core会严格按照你列出的字段生成SQL查询——只获取指定的AdvertiserConcern属性,以及Concern的ContactId和Name。此时Include其实是多余的,EF Core会自动处理关联查询,无需显式声明也能正确获取目标数据。
如果想保留AdvertiserConcern实体结构的同时,只加载Concern的部分属性,不要直接选择整个ac,可以构建包含实体属性和指定关联属性的匿名类型,或者创建DTO类来承载数据,示例如下:
.Select(ac => new { AdvertiserConcern = new AdvertiserConcern { ConcernId = ac.ConcernId, AdvertiserId = ac.AdvertiserId, StartDate = ac.StartDate, EndDate = ac.EndDate, Remark = ac.Remark }, ConcernContactId = ac.Concern.ContactId, ConcernName = ac.Concern.Name })
这样既可以获取完整的AdvertiserConcern数据,又不会加载Concern的多余属性。
内容的提问来源于stack exchange,提问作者Roelant M

