检测到entity framework自我引用循环
我有一个奇怪的错误。 我正在尝试.NET 4.5 Web API,entity framework和MS SQL Server。 我已经创build了数据库并设置了正确的主键和外键。
我创build了一个.edmx模型,并导入了两个表:Employee和Department。 一个部门可以有很多员工,这种关系是存在的。 我使用脚手架选项创build了一个名为EmployeeController的新控制器,以使用Entity Framework创build具有读取/写入操作的API控制器。 在向导中,selectEmployee作为模型,并为数据上下文提供正确的实体。
所创build的方法如下所示:
public IEnumerable<Employee> GetEmployees() { var employees = db.Employees.Include(e => e.Department); return employees.AsEnumerable(); }
当我通过/ api / Employee调用我的API时,出现以下错误:
“ObjectContent`1”types未能序列化内容types为“application / json; … System.InvalidOperationException“,”StackTrace“:null,”InnerException“:{”Message“:”发生了错误“,”ExceptionMessage“:”检测到types为“System.Data.Entity.DynamicProxies .Employee_5D80AD978BC68A1D8BD675852F94E8B550F4CB150ADB8649E8998B7F95422552' 。 Path'[0] .Department.Employees'。“,”ExceptionType“:”Newtonsoft.Json.JsonSerializationException“,”StackTrace“:”…
为什么它是自引用[0]。部门雇员? 这并没有太多的意义。 我希望这发生,如果我有我的数据库中循环引用,但这是一个非常简单的例子。 有什么可能会出错?
那么基于Json.net的默认Json formater的正确答案是将ReferenceLoopHandling
设置为Ignore
。
只需将其添加到Global.asax中的Application_Start
:
HttpConfiguration config = GlobalConfiguration.Configuration; config.Formatters.JsonFormatter .SerializerSettings .ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
这是正确的方法。 它会忽略指向对象的引用。
其他响应集中在更改排除数据或创build门面对象时返回的列表,有时这不是选项。
使用JsonIgnore
属性来限制引用可能很耗时,如果你想从另一个点开始序列化树,那将是一个问题。
发生这种情况是因为您试图直接序列化EF对象集合。 由于部门与员工和部门之间有联系,所以JSON序列化程序将循环地读取d.Employee.Departments.Employee.Departments等内容。
要在序列化之前解决这个问题,请用你想要的道具创build一个匿名types
示例(伪代码):
departments.select(dep => new { dep.Id, Employee = new { dep.Employee.Id, dep.Employee.Name } });
我有同样的问题,并发现你可以应用[JsonIgnore]
属性的导航属性,你不想被序列化。 它仍然会连续化父代和子代实体,但只是避免了自引用循环。
主要问题是序列化与其他实体模型(外键关系)有关系的实体模型。 这个关系导致自引用这将抛出exception,而序列化为json或xml。 有很多select。 不使用自定义模型序列化实体模型。使用Automapper或Valueinjector映射到自定义模型(对象映射)的实体模型数据的值或数据然后返回请求,并将序列化,而没有任何其他问题。 或者你可以序列化实体模型,所以先在实体模型中禁用代理
public class LabEntities : DbContext { public LabEntities() { Configuration.ProxyCreationEnabled = false; }
要保存XML中的对象引用,您有两个选项。 更简单的选项是将[DataContract(IsReference = true)]添加到您的模型类。 IsReference参数启用了oibject引用。 请记住,DataContract使select序列化,因此您还需要将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;} }
在global.asax中使用Json格式
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.All;
以xml格式
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter; var dcs = new DataContractSerializer(typeof(Employee), null, int.MaxValue, false, /* preserveObjectReferences: */ true, null); xml.SetSerializer<Employee>(dcs);
消息错误意味着你有一个自引用循环。
你生产的json就像这个例子(有一个雇员的名单):
[ employee1 : { name: "name", department : { name: "departmentName", employees : [ employee1 : { name: "name", department : { name: "departmentName", employees : [ employee1 : { name: "name", department : { and again and again.... } ] } } ] } }
]
你必须告诉数据库上下文,当你请求某些东西时,你不想获得所有链接的实体。 DbContext的选项是Configuration.LazyLoadingEnabled
我发现的最好的方法是创build一个序列化的上下文:
public class SerializerContext : LabEntities { public SerializerContext() { this.Configuration.LazyLoadingEnabled = false; } }
我也可以考虑为每个控制器/操作添加明确的样本,这里也包括:
即config.SetActualResponseType(typeof(SomeType),“Values”,“Get”);
添加一行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) { } }
我只有一个模型我想用,所以我结束了以下代码:
var JsonImageModel = Newtonsoft.Json.JsonConvert.SerializeObject(Images, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });