EF Core 6.0导航属性未加载,使用Include后触发对象循环异常
问题根因
- 导航属性为null:EF Core默认不会主动加载未显式指定的关联数据,你直接查询
db.Authors.ToListAsync()时,仅会查询Authors表的字段,不会关联查询Books表,所以返回的Books导航属性为null,Books的Author导航属性同理。 - 循环引用异常:当你使用
Include显式加载关联数据后,Author实体包含Books列表,每个Book实体又包含关联的Author导航属性,JSON序列化器在序列化对象时会进入「Author → 包含的Book → Book关联的Author → ...」的无限循环,因此抛出JsonException。
解决方法
方案1:配置JSON序列化忽略循环引用(快速修复)
在最小API的服务注册阶段添加序列化配置,告诉System.Text.Json序列化器忽略循环引用:
var builder = WebApplication.CreateBuilder(args); // 添加数据库注册 builder.Services.AddSqlite<LibraryDb>("Data Source=Library.db"); // 新增JSON序列化配置,忽略循环引用 builder.Services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(options => { options.SerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles; }); var app = builder.Build(); // 修改查询语句,显式加载关联数据 app.MapGet("/authors", async (LibraryDb db) => await db.Authors.Include(a => a.Books).ToListAsync()); app.MapGet("/books", async (LibraryDb db) => await db.Books.Include(b => b.Author).ToListAsync()); app.Run();
方案2:使用DTO传输数据(规范推荐)
定义专门用于接口返回的传输对象,移除循环关联的属性,避免直接返回数据库实体,耦合度更低也更安全:
// 定义DTO public class BookDto { public int Id { get; set; } public string Title { get; set; } // 不需要返回作者信息可以移除,需要的话可以放作者名称/ID,不要放完整Author实体 public string AuthorName { get; set; } } public class AuthorDto { public int Id { get; set; } public string Name { get; set; } public List<BookDto> Books { get; set; } }
查询时做属性映射即可:
app.MapGet("/authors", async (LibraryDb db) => await db.Authors.Include(a => a.Books) .Select(a => new AuthorDto { Id = a.Id, Name = a.Name, Books = a.Books.Select(b => new BookDto { Id = b.Id, Title = b.Title }).ToList() }).ToListAsync());
方案3:给导航属性加JsonIgnore标记(临时方案)
在实体类的循环导航属性上添加[JsonIgnore]特性,序列化时会自动跳过该属性:
public class Book { public int Id { get; set; } public string Title { get; set; } [JsonIgnore] // 序列化时忽略该属性,避免循环 public Author Author { get; set; } }
内容的提问来源于stack exchange,提问作者Inness Chapman
相关产品推荐
相关产品推荐

