EF 4.1 - 代码优先 - JSON循环参考序列化错误

Gui*_*lmi 47 c# serialization json entity-framework asp.net-mvc-3

我得到一个循环引用序列化错误,但据我所知,我没有任何循环引用.我正在从数据库中检索一组订单并将它们作为JSON发送到客户端.所有代码如下所示.

这是错误:

错误

序列化"System.Data.Entity.DynamicProxies.Order_83CECF2AA4DE38232F9077D4B26941AB96BC61230419EA8AC42C9100E6072812"类型的对象时检测到循环引用.描述:执行当前Web请求期间发生未处理的异常.请查看堆栈跟踪以获取有关错误及其源自代码的位置的更多信息.

异常详细信息:System.InvalidOperationException:序列化"System.Data.Entity.DynamicProxies.Order_83CECF2AA4DE38232F9077D4B26941AB96BC61230419EA8AC42C9100E6072812"类型的对象时检测到循环引用.

来源错误:

在执行当前Web请求期间生成了未处理的异常.可以使用下面的异常堆栈跟踪来识别有关异常的起源和位置的信息.

我的课程如下:

订购

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; }
}
Run Code Online (Sandbox Code Playgroud)

患者

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; }
}
Run Code Online (Sandbox Code Playgroud)

认证期限

public class CertificationPeriod
{
    [Key]
    public int CertificationPeriodId { get; set; }
    public DateTime startDate { get; set; }
    public DateTime endDate { get; set; }
    public bool isDeprecated { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

机构

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; }
}
Run Code Online (Sandbox Code Playgroud)

诊断

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; }
}
Run Code Online (Sandbox Code Playgroud)

OrderApprovalStatus

public class OrderApprovalStatus
{
    [Key]
    public int OrderApprovalStatusId { get; set; }
    public string Status { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

用户

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; }

}
Run Code Online (Sandbox Code Playgroud)

注意:地址类是编辑期间的新增功能

地址

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; }
}
Run Code Online (Sandbox Code Playgroud)

执行序列化的代码在这里:

来自OrderController的摘录

    public ActionResult GetAll()
    {
        return Json(ppEFContext.Orders, JsonRequestBehavior.AllowGet);
    }
Run Code Online (Sandbox Code Playgroud)

谢谢

Sla*_*uma 48

您可以尝试virtual从所有导航属性中删除关键字以禁用延迟加载和代理创建,然后使用预先加载来明确加载所需的对象图:

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);
}
Run Code Online (Sandbox Code Playgroud)

参考你之前的帖子看起来你的应用程序似乎并不依赖于延迟加载,因为你在那里引入了虚拟属性来懒惰加载对象图,可能导致现在的序列化问题.

编辑

没有必要virtual从导航属性中删除关键字(这将使模型完全无法进行延迟加载).对于代理令人不安的特定情况,如序列化,它足以禁用代理创建(这也会禁用延迟加载):

ppEFContext.Configuration.ProxyCreationEnabled = false;
Run Code Online (Sandbox Code Playgroud)

这将仅为特定上下文实例禁用代理创建ppEFContext.

(我刚才看到,@ WillC已经在这里提到过了.请为这个编辑提供支持请回答他.)


Wil*_*llC 41

当您知道需要从特定上下文序列化时,可以禁用该特定查询的代理创建,如下所示.这对我有用,并且比修改我的模型类更好.

using (var context = new MeContext())
{
    context.Configuration.ProxyCreationEnabled = false;
    return context.cars.Where(w => w.Brand == "Ferrari")
}
Run Code Online (Sandbox Code Playgroud)

此方法取消了此特定上下文实例的代理对象类型,因此返回的对象是实际的类,因此序列化不是问题.

即:

{Models.car} 
Run Code Online (Sandbox Code Playgroud)

代替

{System.Data.Entity.DynamicProxies.car_231710A36F27E54BC6CE99BB50E0FE3B6BD4462EC??A19695CD1BABB79605296EB} 
Run Code Online (Sandbox Code Playgroud)


Chr*_*oph 9

问题是你实际上正在序列化一个实体框架生成的代理对象.不幸的是,当与JSON序列化程序一起使用时,这会出现一些问题.为了JSON兼容性,您可以考虑将实体映射到特殊的简单POCO类.

  • 这是正确的,您不需要更改EF实体,只需将它们映射到仅供View使用的实体. (3认同)

bdp*_*ish 8

有一个要添加到Entity Framework对象的属性

[ScriptIgnore]
Run Code Online (Sandbox Code Playgroud)

这使代码不执行循环引用.


Joh*_*ski 7

我认为他们已经在最新版本中解决了这个问题.

查看" 序列化和反序列化JSON - >序列化和保留对象引用 " 部分下的帮助文档.

初始化JSON.Net Serializer时设置此设置:

PreserveReferencesHandling = PreserveReferencesHandling.Objects;
Run Code Online (Sandbox Code Playgroud)

所以一个例子是这样的:

var serializerSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects };

string json = JsonConvert.SerializeObject(people, Formatting.Indented, serializerSettings);
Run Code Online (Sandbox Code Playgroud)

我确认这适用于我的代码第一个解决方案,以及导航属性中的循环引用.如果你看看生成的JSON,它应该在任何地方都有"$ id"和"$ ref"属性.


abd*_*rim 6

另一种解决方案是使用匿名类型作为LINQ查询的结果.

在我的项目中,我正在广泛使用延迟加载,并且禁用它不是正确的做法.