You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 17:21:12