Web Api返回含Include的Entity结果时遇循环引用异常求助
解决EF Core查询返回时的JSON循环引用异常
问题原因
你遇到的异常核心是JSON序列化循环引用:实体类的导航属性形成了闭环——Book包含Chapters集合,每个Chapter又持有对应的Book引用,序列化JSON时会无限递归这层关系,最终超出默认深度限制抛出异常。
解决方法
1. 使用DTO(数据传输对象)【推荐】
创建与实体类对应的DTO,只保留需要返回的属性,从根源上避免循环引用,同时避免暴露不必要的实体细节:
// 定义DTO类 public class BookDTO { public string Id { get; set; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public List<ChapterDTO> Chapters { get; set; } = new List<ChapterDTO>(); } public class ChapterDTO { public string Id { get; set; } public string Title { get; set; } public string Description { get; set; } public List<PageNoteDTO> PageNotes { get; set; } = new List<PageNoteDTO>(); } public class PageNoteDTO { public string Id { get; set; } public string Title { get; set; } public string Description { get; set; } }
修改查询代码,将实体投影到DTO:
ServiceResponse<List<BookDTO>> response = new ServiceResponse<List<BookDTO>>(); var booksDto = await _context.Books .Include(x => x.Chapters) .ThenInclude(x => x.PageNotes) .Select(book => new BookDTO { Id = book.Id, Name = book.Name, Description = book.Description, Price = book.Price, Chapters = book.Chapters.Select(chapter => new ChapterDTO { Id = chapter.Id, Title = chapter.Title, Description = chapter.Description, PageNotes = chapter.PageNotes.Select(note => new PageNoteDTO { Id = note.Id, Title = note.Title, Description = note.Description }).ToList() }).ToList() }) .ToListAsync(); response.Data = booksDto; response.Success = true; response.Message = "List of All Books Provided"; return Ok(response);
2. 配置JSON序列化选项
在Program.cs(.NET 6+)中修改JSON序列化配置,允许处理循环引用:
builder.Services.AddControllers() .AddJsonOptions(options => { // 忽略循环引用(直接跳过闭环中的重复属性) options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; // 或者保留引用(会添加$id等标识,适合需要保留关联关系的场景) // options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve; });
这种方法快速但不优雅,会增加返回数据体积或引入序列化标识,不推荐用于对外API。
3. 忽略反向导航属性
在实体类的反向导航属性上添加[JsonIgnore]特性,阻止序列化该属性:
public class Chapter { // 其他属性... [ForeignKey("BookId")] public string BookId { get; set; } [JsonIgnore] // 忽略Book引用,避免序列化循环 public Book Book { get; set; } } public class PageNote { // 其他属性... [ForeignKey("ChapterId")] public string ChapterId { get; set; } [JsonIgnore] // 忽略Chapter引用 public Chapter Chapter { get; set; } }
这种方法简单直接,但不够灵活,后续如果需要返回这些属性就得修改实体类,违反开闭原则。
内容的提问来源于stack exchange,提问作者Scottish Smile
相关产品推荐
相关产品推荐

