EF 4.1 – Code First – JSON循环引用序列化错误
我得到了一个循环引用序列化错误,虽然,据我所知,我没有任何循环引用。 我从数据库中检索一组订单,并以JSON的forms将它们发送到客户端。 所有的代码如下所示。
这是错误的:
错误
序列化“System.Data.Entity.DynamicProxies.Order_83CECF2AA4DE38232F9077D4B26941AB96BC61230419EA8AC42C9100E6072812”types的对象时检测到循环引用。 说明:执行当前Web请求期间发生未处理的exception。 请查看堆栈跟踪,了解有关错误的更多信息以及源代码的来源。
exception详细信息:System.InvalidOperationException:在序列化“System.Data.Entity.DynamicProxies.Order_83CECF2AA4DE38232F9077D4B26941AB96BC61230419EA8AC42C9100E6072812”types的对象时检测到循环引用。
源错误:
在执行当前Web请求期间生成未处理的exception。 有关exception的来源和位置的信息可以使用下面的exception堆栈跟踪来标识。
我的课程如下:
订购
public class Order { [Key] public int OrderId { get; set; } public int PatientId { get; set; } public virtual Patient Patient { get; set; } public int CertificationPeriodId { get; set; } public virtual CertificationPeriod CertificationPeriod { get; set; } public int AgencyId { get; set; } public virtual Agency Agency { get; set; } public int PrimaryDiagnosisId { get; set; } public virtual Diagnosis PrimaryDiagnosis { get; set; } public int ApprovalStatusId { get; set; } public virtual OrderApprovalStatus ApprovalStatus { get; set; } public int ApproverId { get; set; } public virtual User Approver { get; set; } public int SubmitterId { get; set; } public virtual User Submitter { get; set; } public DateTime ApprovalDate { get; set; } public DateTime SubmittedDate { get; set; } public Boolean IsDeprecated { get; set; } }
患者
public class Patient { [Key] public int PatientId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string MiddleInitial { get; set; } public bool IsMale; public DateTime DateOfBirth { get; set; } public int PatientAddressId { get; set; } public Address PatientAddress { get; set; } public bool IsDeprecated { get; set; } }
authentication期限
public class CertificationPeriod { [Key] public int CertificationPeriodId { get; set; } public DateTime startDate { get; set; } public DateTime endDate { get; set; } public bool isDeprecated { get; set; } }
机构
public class Agency { [Key] public int AgencyId { get; set; } public string Name { get; set; } public int PatientAddressId { get; set; } public virtual Address Address { get; set; } }
诊断
public class Diagnosis { [Key] public int DiagnosisId { get; set; } public string Icd9Code { get; set; } public string Description { get; set; } public DateTime DateOfDiagnosis { get; set; } public string Onset { get; set; } public string Details { get; set; } }
OrderApprovalStatus
public class OrderApprovalStatus { [Key] public int OrderApprovalStatusId { get; set; } public string Status { get; set; } }
用户
public class User { [Key] public int UserId { get; set; } public string Login { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string NPI { get; set; } public string Email { get; set; } }
注:地址类是编辑期间的新增加
地址
public class Address { [Key] public int AddressId { get; set; } public string StreetAddress { get; set; } public string City { get; set; } public string State { get; set; } public string Zip { get; set; } public string Phone { get; set; } public string Title { get; set; } public string Label { get; set; } }
执行序列化的代码在这里:
摘自OrderController
public ActionResult GetAll() { return Json(ppEFContext.Orders, JsonRequestBehavior.AllowGet); }
谢谢
您可以尝试从所有导航属性中删除virtual
关键字,以禁用延迟加载和代理创build,然后使用意外加载来显式加载所需的对象图:
public ActionResult GetAll() { return Json(ppEFContext.Orders .Include(o => o.Patient) .Include(o => o.Patient.PatientAddress) .Include(o => o.CertificationPeriod) .Include(o => o.Agency) .Include(o => o.Agency.Address) .Include(o => o.PrimaryDiagnosis) .Include(o => o.ApprovalStatus) .Include(o => o.Approver) .Include(o => o.Submitter), JsonRequestBehavior.AllowGet); }
参考你以前的post ,看起来你的应用程序并不依赖懒加载,因为你在那里引入了虚拟属性来懒惰地加载对象图,可能会导致现在的序列化问题。
编辑
没有必要从导航属性中删除virtual
关键字(这将使模型的延迟加载完全不可能)。 这足以禁用代理创build(也会禁用延迟加载),以便代理受到干扰的特定情况下,如序列化:
ppEFContext.Configuration.ProxyCreationEnabled = false;
这将仅禁用特定上下文实例ppEFContext
代理创build。
(我刚刚看过,@WillC已经在这里提到过了,请给他回答。
当你知道你需要从特定的上下文进行序列化时,你可以像下面那样禁用特定查询的代理创build。 这对我来说,并比修改我的模型类更好。
using (var context = new MeContext()) { context.Configuration.ProxyCreationEnabled = false; return context.cars.Where(w => w.Brand == "Ferrari") }
这种方法取消了上下文的特定实例的代理对象types,所以返回的对象是实际的类,因此序列化不成问题。
即:
{Models.car}
代替
{System.Data.Entity.DynamicProxies.car_231710A36F27E54BC6CE99BB50E0FE3B6BD4462ECA19695CD1BABB79605296EB}
问题是你实际上是序列化一个entity framework生成的代理对象。 不幸的是,这与使用JSON序列化程序时有一些问题。 为了JSON兼容性,您可能会考虑将您的实体映射到特殊的简单POCO类。
有一个要添加到entity framework对象的属性
[ScriptIgnore]
这使得代码不能执行循环引用。
我认为他们已经在最新版本中解决了这个问题。
请参阅“ 序列化和反序列化JSON – >序列化和保留对象引用 ”一节下的帮助文档 。
初始化JSON.Net序列化程序时设置此设置:
PreserveReferencesHandling = PreserveReferencesHandling.Objects;
所以一个例子是这样的:
var serializerSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects }; string json = JsonConvert.SerializeObject(people, Formatting.Indented, serializerSettings);
我证实,这与我的代码的第一个解决scheme,并在导航属性中的循环引用。 如果你看看产生的JSON,它应该有“$ id”和“$ ref”属性。
另一种解决scheme是使用匿名types作为LINQ查询的结果。
在我的项目中,我广泛使用了延迟加载,禁用它并不是正确的做法。
另一种解决scheme是,如果只需要对象中的某些值,则构build一个匿名类并将其返回,如下例所示:
public JsonResult AjaxFindByName(string term) { var customers = context.Customers .Where(c => c.Name.ToUpper().Contains(term.ToUpper())).Take(10) .AsEnumerable() .Select(c => new { value = c.Name, SSN = String.Format(@"{0:000\-00\-0000}", c.SSN), CustomerID = c.CustomerID }); return Json(customers, JsonRequestBehavior.AllowGet); }
循环引用发生是因为您使用对象上的急切加载。
你有几个方法:
- 当您加载您的查询(linq或lambda)时closures加载加载DbContext.Configuration.ProxyCreationEnabled = false;
- 从Domainmodel中删除虚拟关键字
- 分离对象(=没有渴望的加载function和没有代理)
- Repository.Detach(entityObject)
- DbContext.Entry(entityObject).EntityState = EntityState.Detached
- 克隆属性
- 你可以使用像AutoMapper这样的东西来克隆对象,不要使用ICloneable接口,因为它也克隆了对象中的ProxyProperties,所以这是行不通的。
- 如果您正在构buildAPI,请尝试使用不同configuration的separte项目(不返回代理)
PS。 代理是EF从entity framework加载它时创build的对象。 简而言之:这意味着它保存了原始值和更新值,以便稍后可以更新。 它处理其他的事情;-)
对于那些使用代理EF / Linq2SQL类我的解决scheme是简单地删除我的子实体的父引用。
所以在我的模型中,我select了关系,并将Parent引用改为Internal,而不是Public。
可能不是一个理想的解决scheme,但为我工作。
您可以删除virtual
关键字:
public virtual Patient Patient { get; set; }
public virtual Patient Patient { get; set; }
– > public Patient Patient { get; set; }
public Patient Patient { get; set; }
请记住,当你删除虚拟关键字,延迟加载将被closures。
- 在C#中,你如何单向序列化的非序列化?
- C / C ++中double / floattypes的二进制序列化的可移植性
- 如何处理Findbugs“在序列化类中的非暂时性不可序列化的实例字段”?
- DataContractSerializer vs XmlSerializer:每个序列化程序的优点和缺点
- 如何反序列化对象,如果它被移动到另一个包或重命名?
- 我如何PHP序列化一个jQuery序列化的forms?
- 如何序列化没有格式的JObject?
- “Type not expected”,使用DataContractSerializer – 但它只是一个简单的类,没有有趣的东西?
- 如何使$ .serialize()考虑到那些禁用:input元素?