Lyd*_*don 100 c# serialization entity-framework-4 asp.net-web-api
我有一个奇怪的错误.我正在试验.NET 4.5 Web API,实体框架和MS SQL Server.我已经创建了数据库并设置了正确的主键和外键以及关系.
我创建了一个.edmx模型并导入了两个表:Employee和Department.一个部门可以有很多员工,这种关系存在.我使用scaffolding选项创建了一个名为EmployeeController的新控制器,以使用Entity Framework创建一个带有读/写操作的API控制器.在向导中,选择Employee作为模型,并选择数据上下文的正确实体.
创建的方法如下所示:
public IEnumerable<Employee> GetEmployees()
{
var employees = db.Employees.Include(e => e.Department);
return employees.AsEnumerable();
}
Run Code Online (Sandbox Code Playgroud)
当我通过/ api/Employee调用我的API时,出现此错误:
'ObjectContent`1'类型无法序列化内容类型'application/json的响应主体; ... ... System.InvalidOperationException","StackTrace":null,"InnerException":{"Message":"发生错误.","ExceptionMessage":"使用类型'System.Data.Entity.DynamicProxies检测到自引用循环.Employee_5D80AD978BC68A1D8BD675852F94E8B550F4CB150ADB8649E8998B7F95422552' .Path'[0] .Department.Employees'.","ExceptionType":"Newtonsoft.Json.JsonSerializationException","StackTrace":"...
为什么它自引用[0] .Department.Employees?这并没有多大意义.如果我在我的数据库中进行循环引用,我希望这会发生,但这是一个非常简单的例子.怎么可能出错?
小智 155
那么基于Json.net的默认Json格式化器的正确答案是设置ReferenceLoopHandling为Ignore.
只需将其添加到Application_StartGlobal.asax中:
HttpConfiguration config = GlobalConfiguration.Configuration;
config.Formatters.JsonFormatter
.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
Run Code Online (Sandbox Code Playgroud)
这是正确的方法.它将忽略指向对象的引用.
其他响应的重点是通过排除数据或通过制作Facade对象来更改返回的列表,有时这不是一个选项.
使用该JsonIgnore属性来限制引用可能非常耗时,并且如果要从另一个点开始序列化树将是一个问题.
Nic*_*aub 52
发生这种情况是因为您正在尝试直接序列化EF对象集合.由于部门与员工和员工之间存在关联,因此JSON序列化程序将循环读取d.Employee.Departments.Employee.Departments等...
要在序列化之前修复此问题,请创建一个包含所需道具的匿名类型
示例(伪)代码:
departments.select(dep => new {
dep.Id,
Employee = new {
dep.Employee.Id, dep.Employee.Name
}
});
Run Code Online (Sandbox Code Playgroud)
Pio*_*rek 18
我知道这个问题很老了,但它仍然很受欢迎,我看不到ASP.net Core的任何解决方案.
我是ASP.net Core的情况,需要JsonOutputFormatter在Startup.cs文件中添加新内容:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.OutputFormatters.Clear();
options.OutputFormatters.Add(new JsonOutputFormatter(new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
}, ArrayPool<char>.Shared));
});
//...
}
Run Code Online (Sandbox Code Playgroud)
实现它之后,JSON序列化器将简单地忽略循环引用.它的含义是:它将返回null而不是无限地加载彼此引用的对象.
没有以上解决方案使用
var employees = db.Employees.ToList();
Run Code Online (Sandbox Code Playgroud)
会加载Employees并与它们相关Departments.
设置ReferenceLoopHandling为之后Ignore,Departments除非将其包含在查询中,否则将设置为null:
var employees = db.Employees.Include(e => e.Department);
Run Code Online (Sandbox Code Playgroud)
另外,请记住它将清除所有OutputFormatters,如果您不希望您可以尝试删除此行:
options.OutputFormatters.Clear();
Run Code Online (Sandbox Code Playgroud)
但self referencing loop由于某些原因,删除它会导致我的情况再次出现异常.
主要问题是序列化与其他实体模型(外键关系)有关系的实体模型.这种关系导致自引用,这将在序列化为json或xml时抛出异常.有很多选择.如果不通过使用自定义models.Values或数据从映射到定制机型(对象映射)采用实体模型数据序列化实体模型Automapper或Valueinjector然后返回请求,它会序列化,没有任何其他问题.或者您可以序列化实体模型,因此首先在实体模型中禁用代理
public class LabEntities : DbContext
{
public LabEntities()
{
Configuration.ProxyCreationEnabled = false;
}
Run Code Online (Sandbox Code Playgroud)
要保留XML中的对象引用,您有两个选择.更简单的选项是将[DataContract(IsReference = true)]添加到模型类中.IsReference参数启用oibject引用.请记住,DataContract使序列化选择加入,因此您还需要将DataMember属性添加到属性中:
[DataContract(IsReference=true)]
public partial class Employee
{
[DataMember]
string dfsd{get;set;}
[DataMember]
string dfsd{get;set;}
//exclude the relation without giving datamember tag
List<Department> Departments{get;set;}
}
Run Code Online (Sandbox Code Playgroud)
在global.asax中以Json格式
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.All;
Run Code Online (Sandbox Code Playgroud)
以xml格式
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
var dcs = new DataContractSerializer(typeof(Employee), null, int.MaxValue,
false, /* preserveObjectReferences: */ true, null);
xml.SetSerializer<Employee>(dcs);
Run Code Online (Sandbox Code Playgroud)
小智 8
消息错误意味着您有一个自引用循环.
你生成的json就像这个例子(有一个雇员的列表):
[
employee1 : {
name: "name",
department : {
name: "departmentName",
employees : [
employee1 : {
name: "name",
department : {
name: "departmentName",
employees : [
employee1 : {
name: "name",
department : {
and again and again....
}
]
}
}
]
}
}
Run Code Online (Sandbox Code Playgroud)
]
您必须告诉db上下文,当您请求某些内容时,您不希望获得所有链接的实体.DbContext的选项是Configuration.LazyLoadingEnabled
我找到的最好方法是创建序列化的上下文:
public class SerializerContext : LabEntities
{
public SerializerContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
Run Code Online (Sandbox Code Playgroud)
小智 7
Configuration.ProxyCreationEnabled = false;在上下文模型部分类定义的构造函数中添加一行.
public partial class YourDbContextModelName : DbContext
{
public YourDbContextModelName()
: base("name=YourDbContextConn_StringName")
{
Configuration.ProxyCreationEnabled = false;//this is line to be added
}
public virtual DbSet<Employee> Employees{ get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
Run Code Online (Sandbox Code Playgroud)
我只有一个我想使用的模型,所以我最终得到了以下代码:
var JsonImageModel = Newtonsoft.Json.JsonConvert.SerializeObject(Images, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
Run Code Online (Sandbox Code Playgroud)
小智 6
我刚刚在我的 .net core 网站上遇到了同样的问题。接受的答案对我不起作用,但我发现 ReferenceLoopHandling.Ignore 和 PreserveReferencesHandling.Objects 的组合修复了它。
//serialize item
var serializedItem = JsonConvert.SerializeObject(data, Formatting.Indented,
new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
Run Code Online (Sandbox Code Playgroud)
如果你想在Blazor(ASP.NET核心托管)模板来更改此设置,您需要将以下传递到AddNewtonsoftJson通话中Startup.cs的Server项目:
services.AddMvc().AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore
);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
89521 次 |
| 最近记录: |