Naz*_*lyp 119 c# entity-framework-core asp.net-core
我有 2 个实体,它们是一对多相关的
public class Restaurant {
public int RestaurantId {get;set;}
public string Name {get;set;}
public List<Reservation> Reservations {get;set;}
...
}
Run Code Online (Sandbox Code Playgroud)
public class Reservation{
public int ReservationId {get;set;}
public int RestaurantId {get;set;}
public Restaurant Restaurant {get;set;}
}
Run Code Online (Sandbox Code Playgroud)
如果我尝试使用我的 api 预订餐厅
var restaurants = await _dbContext.Restaurants
.AsNoTracking()
.AsQueryable()
.Include(m => m.Reservations).ToListAsync();
.....
Run Code Online (Sandbox Code Playgroud)
我收到错误响应,因为对象包含彼此的引用。有相关帖子建议创建单独的模型 或添加NewtonsoftJson 配置
问题是我不想创建单独的模型,第二个建议没有帮助。有没有办法在没有循环关系的情况下加载数据?*
System.Text.Json.JsonException: 检测到不支持的可能的对象循环。这可能是由于循环或对象深度大于最大允许深度 32。 at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerCycleDetected(Int32 maxDepth) at System.Text.Json.JsonSerializer.Write(Utf8JsonWriter writer , Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, Object value, Type inputType, JsonSerializerOptions options, CancellationToken cancelationToken) at Microsoft.AspNetCore.Mvc.OutputFormatterSystem.TextJFormatterSystem. Microsoft.AspNetCore.Mvc 上的 WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)。
*
Rya*_*yan 180
我已经在一个新项目中尝试过你的代码,在首先为 3.0安装软件包Microsoft.AspNetCore.Mvc.NewtonsoftJson后,第二种方法似乎工作得很好
services.AddControllersWithViews()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
Run Code Online (Sandbox Code Playgroud)
尝试一个新项目并比较差异。
小智 145
.NET Core 3.1 安装包 Microsoft.AspNetCore.Mvc.NewtonsoftJson(来自https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.NewtonsoftJson/)
Startup.cs 添加服务
services.AddControllers().AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
Run Code Online (Sandbox Code Playgroud)
Ogg*_*las 64
更新:
使用 .NET 6,有一个选项可以System.Text.Json忽略循环引用,如下所示:
JsonSerializerOptions options = new()
{
ReferenceHandler = ReferenceHandler.IgnoreCycles,
WriteIndented = true
};
Run Code Online (Sandbox Code Playgroud)
问题ReferenceHandler.Preserve是 JSON 键以 为前缀$,这可能会导致一些问题。
例子System.Text.Json ReferenceHandler.IgnoreCycles:
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SerializeIgnoreCycles
{
public class Employee
{
public string Name { get; set; }
public Employee Manager { get; set; }
public List<Employee> DirectReports { get; set; }
}
public class Program
{
public static void Main()
{
Employee tyler = new()
{
Name = "Tyler Stein"
};
Employee adrian = new()
{
Name = "Adrian King"
};
tyler.DirectReports = new List<Employee> { adrian };
adrian.Manager = tyler;
JsonSerializerOptions options = new()
{
ReferenceHandler = ReferenceHandler.IgnoreCycles,
WriteIndented = true
};
string tylerJson = JsonSerializer.Serialize(tyler, options);
Console.WriteLine($"Tyler serialized:\n{tylerJson}");
Employee tylerDeserialized =
JsonSerializer.Deserialize<Employee>(tylerJson, options);
Console.WriteLine(
"Tyler is manager of Tyler's first direct report: ");
Console.WriteLine(
tylerDeserialized.DirectReports[0].Manager == tylerDeserialized);
}
}
}
// Produces output like the following example:
//
//Tyler serialized:
//{
// "Name": "Tyler Stein",
// "Manager": null,
// "DirectReports": [
// {
// "Name": "Adrian King",
// "Manager": null,
// "DirectReports": null
// }
// ]
//}
//Tyler is manager of Tyler's first direct report:
//False
Run Code Online (Sandbox Code Playgroud)
来源:
示例为Newtonsoft.Json.ReferenceLoopHandling.Ignore
public class Employee
{
public string Name { get; set; }
public Employee Manager { get; set; }
}
Employee joe = new Employee { Name = "Joe User" };
Employee mike = new Employee { Name = "Mike Manager" };
joe.Manager = mike;
mike.Manager = mike;
string json = JsonConvert.SerializeObject(joe, Formatting.Indented, new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
Console.WriteLine(json);
// {
// "Name": "Joe User",
// "Manager": {
// "Name": "Mike Manager"
// }
// }
Run Code Online (Sandbox Code Playgroud)
原来的:
我从使用ADO.NET Entity Framework使用带有操作的 API 控制器创建的控制器中的默认 POST 方法收到此错误。
return CreatedAtAction("GetLearningObjective", new { id = learningObjective.Id }, learningObjective);
Run Code Online (Sandbox Code Playgroud)
System.Text.Json.JsonException:检测到可能的对象循环。这可能是由于循环造成的,或者对象深度大于允许的最大深度 32。请考虑在 JsonSerializerOptions 上使用 ReferenceHandler.Preserve 来支持循环。在 System.Text.Json.ThrowHelper.ThrowJsonException_SerializerCycleDetected(Int32 maxDepth)
HttpGet当直接从Postman或浏览器调用时,它工作没有问题。通过编辑解决了这个问题Startup.cs-services.AddControllers()像这样:
services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
});
Run Code Online (Sandbox Code Playgroud)
你也可以这样解决:
services.AddControllers(options =>
{
options.OutputFormatters.RemoveType<SystemTextJsonOutputFormatter>();
options.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
ReferenceHandler = ReferenceHandler.Preserve,
}));
});
Run Code Online (Sandbox Code Playgroud)
如何在 System.Text.Json 中保留引用并处理或忽略循环引用
小智 40
我没有使用 NewtonsoftJson,而是使用System.Text.Json.Serialization。
对于.NET Core 3.1
在文件Startup.cs中:
public void ConfigureServices(IServiceCollection services)
{
..........
.......
services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
options.JsonSerializerOptions.WriteIndented = true;
});
}
Run Code Online (Sandbox Code Playgroud)
对于.NET 6
在文件Program.cs中:
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
options.JsonSerializerOptions.WriteIndented = true;
});
Run Code Online (Sandbox Code Playgroud)
tim*_*mur 15
在启动时设置 JSON 序列化选项可能是首选方式,因为您将来可能会遇到类似的情况。同时,您可以尝试将数据属性添加到您的模型中,这样它就不会被序列化:https : //www.newtonsoft.com/json/help/html/PropertyJsonIgnore.htm
public class Reservation{
public int ReservationId {get;set;}
public int RestaurantId {get;set;}
[JsonIgnore]
public Restaurant Restaurant {get;set;}
}
Run Code Online (Sandbox Code Playgroud)
小智 13
正如Jozkee在已接受答案的评论中所述,.NET 6 包含ReferenceHandler.IgnoreCycles在System.Text.Json.
这就是我在不安装 Newtonsoft.Json 的情况下解决此问题的方法,并通过将以下内容添加到 .NET 6 来使用 .NET 6 的新功能Program.cs。
builder.Services.AddControllersWithViews()
.AddJsonOptions(options => options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
Run Code Online (Sandbox Code Playgroud)
对于那些不确定对象循环是什么的人来说,这是一篇关于对象循环的有用文章。
小智 10
这使用 System.Text.Json
var options = new JsonSerializerOptions()
{
MaxDepth = 0,
IgnoreNullValues = true,
IgnoreReadOnlyProperties = true
};
Run Code Online (Sandbox Code Playgroud)
使用选项进行序列化
objstr = JsonSerializer.Serialize(obj,options);
Run Code Online (Sandbox Code Playgroud)
MinimalAPI 使用这个:
builder.Services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(options =>
{
options.SerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
});
Run Code Online (Sandbox Code Playgroud)
对于其他未发现其他解决方案有效的人,您实际上需要分析完整的调用堆栈,并查看是否有任何async调用未await在预期等待的位置进行。这是所提到问题的实际问题。
例如,考虑以下方法 in MyAppService,它调用async Task<int>of MyOtherService:
public async Task<int> Create(InputModel input)
{
var id = _myOtherService.CreateAndGetIdAsync(input);
return Created("someUri", id);
}
Run Code Online (Sandbox Code Playgroud)
如果CreateAndGetIdAsyncmethod 是async Task,则对上面的 Create 方法的调用将通过问题中提到的给定异常。这是因为序列化会按id原样中断,但实际上Task<int>不会。int因此,必须await在返回响应之前。
附加说明: 这里还需要注意一件事,即使出现此异常,它也不会影响数据库操作。即,在我上面的示例中,数据库操作将成功。类似地,正如OP中提到的,我正在使用的ORM并没有抛出异常,但这个异常是稍后在调用堆栈的序列中抛出的(在调用者之一中)。
对于 .NET Core 6,将其添加到Program.cs解决了我的问题:
builder.Services.AddControllers().AddJsonOptions(x =>
x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);Run Code Online (Sandbox Code Playgroud)
由于 EF Core 会自动修复导航属性,因此最终可能会在对象图中出现循环。例如,加载博客及其相关帖子将生成引用帖子集合的博客对象。每篇文章都会有对博客的引用。
https://learn.microsoft.com/en-us/ef/core/querying/lated-data/serialization
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMvc()
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
...
}
Run Code Online (Sandbox Code Playgroud)
public void ConfigureServices(IServiceCollection services)
{
...
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
});
...
}
Run Code Online (Sandbox Code Playgroud)
不要将数据库模型直接暴露给控制器,这可能会在将来带来一些漏洞。您可以创建从模型到 Dto 类或类似内容的映射。这样您就不需要禁用周期检测机制,因为它对于防止问题很有用。
小智 6
经过几个小时的调试,它确实有一个简单的解决方案。我发现这个链接很有帮助。
这个错误是因为:
ASP.NET Core 3.0 及以上版本中使用的默认 JSON 序列化程序。
ASP.NET Core 3.0 删除了对 JSON.NET 的依赖,并使用它自己的 JSON 序列化程序,即“System.Text.Json”。
我能够解决添加对 NewtonsoftJson Nuget 包的引用的问题,
Run Code Online (Sandbox Code Playgroud)PM> Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson -Version 3.1.2并更新 Startup.cs 如下,
Run Code Online (Sandbox Code Playgroud)PM> Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson -Version 3.1.2
System.Text.Json 序列化程序当前不支持 ReferenceLoopHandling
public class Reservation{
public int ReservationId {get;set;}
public int RestaurantId {get;set;}
[JsonIgnore]
public Restaurant Restaurant {get;set;}
Run Code Online (Sandbox Code Playgroud)
以上也有效。但我更喜欢以下
services.AddControllers().AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
Run Code Online (Sandbox Code Playgroud)
因为首先我们需要将属性添加到我们可能有循环引用的所有模型中。
| 归档时间: |
|
| 查看次数: |
120413 次 |
| 最近记录: |