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

.NET Core 3.0配置Newtonsoft.Json仍遇自引用循环错误求助

解决Entity Framework自引用循环序列化错误

看起来你已经尝试配置ReferenceLoopHandling.Ignore但还是碰到了循环引用的问题,我来帮你分析原因和可行的解决办法:

问题根源

你在控制器中直接返回了EF的Department实体,而Department关联了Company,Company又包含Departments集合(从错误路径Departments[0]可以看出)。虽然你配置了忽略循环引用,但要么是配置没有正确生效,要么是EF加载的关联数据触发了序列化时的循环检测逻辑。

解决方案

1. 确保NewtonsoftJson配置正确生效

你的Startup.cs中同时调用了AddMvc()和AddControllers()并重复添加了NewtonsoftJson配置,这可能导致配置冲突或未正确应用。ASP.NET Core 3.0+中AddControllers()已经包含了MVC核心功能,建议合并配置:

替换原来的两段配置:

services.AddMvc(option => option.EnableEndpointRouting = false)
    .SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
    .AddNewtonsoftJson(opt => opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
services.AddControllers()
    .AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);

改为:

services.AddControllers(opt => opt.EnableEndpointRouting = false)
    .SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
    .AddNewtonsoftJson(x => 
        x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore
    );

2. 启用PreserveReferencesHandling(快速临时方案)

如果上面的配置仍不生效,可以尝试让NewtonsoftJson主动处理循环引用而非忽略,通过添加PreserveReferencesHandling:

services.AddControllers(opt => opt.EnableEndpointRouting = false)
    .SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
    .AddNewtonsoftJson(x => {
        x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
        x.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
    });

这种方式会给每个序列化对象添加$id属性,避免无限递归,但返回的JSON会包含额外元数据。

3. 使用DTO(数据传输对象)——推荐的长期方案

直接返回EF实体不仅会导致循环引用,还可能暴露敏感/不必要字段,不符合分层架构最佳实践。建议创建DTO封装需要返回给前端的数据:

首先创建对应的DTO类:

// DepartmentDto.cs
public class DepartmentDto
{
    public string UserName { get; set; }
    public string DateResult { get; set; }
    public string RestDay { get; set; }
    public string CompanyName { get; set; }
    // 仅添加前端需要的字段,不要包含导航属性
}

// CompanyDto.cs(如果需要返回公司信息)
public class CompanyDto
{
    public string CompanyName { get; set; }
    public string CurrentDepartmentName { get; set; }
    // 其他需要的字段
}

然后在控制器中将实体映射到DTO:

[HttpPost]
public async Task<IActionResult> LoginTablet([FromBody] LoginViewModel model)
{
    try
    {
        var department = await _departmentRepository.FindFilterIncludeAsync(x => x.UserName == model.UserName, x => x.Company);
        if (department == null) return BadRequestResult("Sistemde Böyle Bir Kullanıcı Bulunamadı");
        // ... 其他校验逻辑

        // 映射到DTO
        var departmentDto = new DepartmentDto
        {
            UserName = department.UserName,
            DateResult = department.DateResult,
            RestDay = department.RestDay,
            CompanyName = department.Company.CompanyName
        };

        var companyDto = new CompanyDto
        {
            CompanyName = department.Company.CompanyName,
            CurrentDepartmentName = department.Name
        };

        return OkResult(new { department = departmentDto, token = _tokenService.GetJWTToken(department.Company) });
    }
    catch (Exception ex)
    {
        return BadRequestResult("Beklenmedik Bir Hata Meydana Geldii : " + ex.Message);
    }
}

这种方式完全避免了循环引用,同时让你精确控制返回的数据,是最推荐的解决方案。

4. 限制EF加载的关联数据

如果不想用DTO,也可以在查询时只加载需要的字段,而非整个关联实体:

var department = await _departmentRepository
    .FindFilterAsync(x => x.UserName == model.UserName)
    .Select(x => new 
    {
        x.Id,
        x.UserName,
        x.Password,
        x.LicenceLastTime,
        x.Active,
        x.Name,
        CompanyName = x.Company.CompanyName
    })
    .FirstOrDefaultAsync();

这样查询出的是匿名对象,不包含导航属性,自然不会有循环引用问题,但这种方式灵活性差,维护成本高。

内容的提问来源于stack exchange,提问作者alkincakiralar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:37:19