如何在Entity Framework中正确使用.Include()关联多张表
问题:EF Core查询Item时关联获取SetContent及对应Set数据
现有三个EF Core模型类:
public class Set { public int Id { get; set; } public string Name { get; set; } public string Code { get; set; } public DateTime? ReleaseDate { get; set; } public string Type { get; set; } public string? Series { get; set; } public string? Description { get; set; } public ICollection<SetContent> Content { get; set; } } public class SetContent { public int Id { get; set; } public int SetId { get; set; } public string? SetCode { get; set; } public string? SetRarity { get; set; } public string? SetRarityCode { get; set; } public int ItemId { get; set; } public virtual Item Item { get; set; } } public class Item { public int Id { get; set; } public string Name { get; set; } public string Type { get; set; } public string Desc { get; set; } public string ImageBig { get; set; } public string ImageSmall { get; set; } public ICollection<SetContent> Sets { get; set; } = new List<SetContent>(); }
需求是在Item详情页中,查询Item时同时获取关联的SetContent数据,以及每个SetContent对应的Set数据,最终得到如下结构的DTO:
{ "id": 0, "name": "string", "type": "string", "desc": "string", "imageBig": "string", "imageSmall": "string", "sets": [ { "id": 0, "setId": 0, "setCode": "string", "setRarity": "string", "setRarityCode": "string", "SetName": "string", "ReleaseDate": "DateTime" } ] }
之前尝试的两种查询方式均存在问题:
- 仅通过
Include(_c => _c.Sets)只能拿到SetContent数据,无法获取关联的Set信息 - 手动加载
Sets集合存在数据过时、引用循环空值的问题
解决方案
1. 补充模型导航属性(推荐)
在SetContent类中添加对Set的导航属性,让EF Core能识别完整的关联关系:
public class SetContent { public int Id { get; set; } public int SetId { get; set; } public string? SetCode { get; set; } public string? SetRarity { get; set; } public string? SetRarityCode { get; set; } public int ItemId { get; set; } public virtual Item Item { get; set; } // 添加Set导航属性 public virtual Set Set { get; set; } }
2. 使用Include+ThenInclude加载多层关联数据
通过嵌套的Include和ThenInclude,一次性加载Item、关联的SetContent,以及每个SetContent对应的Set数据:
var item = await _context.Items .Include(i => i.Sets) .ThenInclude(sc => sc.Set) // 关联加载SetContent对应的Set实体 .FirstOrDefaultAsync(i => i.Id == id);
3. 配置DTO映射(若使用AutoMapper)
在AutoMapper配置中,指定从SetContent和关联的Set中提取所需字段:
// 假设SetContent对应的DTO为SetContentDto CreateMap<SetContent, SetContentDto>() .ForMember(dest => dest.SetName, opt => opt.MapFrom(src => src.Set.Name)) .ForMember(dest => dest.ReleaseDate, opt => opt.MapFrom(src => src.Set.ReleaseDate)); CreateMap<Item, ItemReadOnlyDto>() .ForMember(dest => dest.sets, opt => opt.MapFrom(src => src.Sets));
4. 处理序列化引用循环(可选)
如果JSON序列化时出现引用循环错误,可在项目配置中忽略循环引用:
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; });
5. 直接投影构造DTO(高效替代方案)
无需加载完整实体,直接通过LINQ投影构造目标DTO,减少不必要的数据查询:
var itemDto = await _context.Items .Where(i => i.Id == id) .Select(i => new ItemReadOnlyDto { Id = i.Id, Name = i.Name, Type = i.Type, Desc = i.Desc, ImageBig = i.ImageBig, ImageSmall = i.ImageSmall, sets = i.Sets.Select(sc => new SetContentDto { Id = sc.Id, SetId = sc.SetId, SetCode = sc.SetCode, SetRarity = sc.SetRarity, SetRarityCode = sc.SetRarityCode, SetName = sc.Set.Name, ReleaseDate = sc.Set.ReleaseDate }).ToList() }) .FirstOrDefaultAsync();
内容的提问来源于stack exchange,提问作者Avalari
相关产品推荐
相关产品推荐

