代码优先- JSON循环引用序列化错误

本文关键字:引用 序列化 错误 循环 JSON 代码 | 更新日期: 2023-09-27 17:49:15

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

错误:

误差

检测到循环引用类型的对象序列化时"System.Data.Entity.DynamicProxies.Order_83CECF2AA4DE38232F9077D4B26941AB96BC61230419EA8AC42C9100E6072812"。描述:一个未处理的异常的执行过程中发生当前的web请求。请参阅堆栈跟踪以获取有关的更多信息错误和它的起源代码。

异常细节:系统。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; }
}
病人

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

认证期

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

注:地址类是编辑时新增的

<

地址/strong>

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

谢谢

代码优先- JSON循环引用序列化错误

你可以尝试从所有导航属性中删除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);
}

参考你之前的文章,看起来你的应用程序不依赖于延迟加载,因为你在那里引入了虚拟属性来延迟加载对象图,现在可能会导致序列化问题。

编辑

没有必要从导航属性中删除virtual关键字(这将使模型完全不可能进行延迟加载)。对于干扰代理的特定情况,例如序列化,禁用代理创建(也禁用延迟加载)就足够了:

ppEFContext.Configuration.ProxyCreationEnabled = false;

禁用仅针对特定的上下文实例ppEFContext创建代理。

我刚刚看到,@WillC已经在这里提到了。

当您知道您需要从特定上下文中序列化时,您可以像下面这样禁用该特定查询的代理创建。这对我来说很有效,比修改我的模型课要好。

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

这种方法去掉了上下文的这个特定实例的代理对象类型,因此返回的对象是实际的类,因此序列化不是问题。

ie:

{Models.car} 

代替

{System.Data.Entity.DynamicProxies.car_231710A36F27E54BC6CE99BB50E0FE3B6BD4462EC‌​A19695CD1BABB79605296EB} 

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

有一个属性要添加到实体框架对象

[ScriptIgnore]

这使得代码不执行循环引用。

我想他们已经在最新版本中修复了这个问题。

查看帮助文档"序列化和反序列化JSON ->序列化和保留对象引用"一节。

在初始化JSON时设置此设置。净序列化器:

PreserveReferencesHandling = PreserveReferencesHandling.Objects;

一个例子是:

var serializerSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects };
string json = JsonConvert.SerializeObject(people, Formatting.Indented, serializerSettings);

我验证了这适用于我的代码第一个解决方案,以及导航属性中的循环引用。如果您查看结果JSON,它应该到处都有"$id"answers"$ref"属性。

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

在我的项目中,我广泛使用延迟加载,禁用它不是正确的事情。

另一种解决方案是,如果只需要对象中的某些值,则构建一个匿名类并返回它,如下面的示例:

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)时关闭急切加载dbcontext . configuration . proxycreateenabled = false;
  • 从Domainmodel
  • 中删除虚拟关键字
  • 分离对象(=没有即时加载功能&没有代理)
    • Repository.Detach (entityObject)
    • DbContext.Entry (entityObject)。EntityState = EntityState。分离
  • 克隆属性
    • 你可以使用像AutoMapper这样的东西来克隆对象,不要使用iclonable接口,因为它也克隆对象中的ProxyProperties,所以这将不起作用。
  • 如果您正在构建API,请尝试使用具有不同配置的单独项目(不返回代理)

p。代理是EF从实体框架加载时创建的对象。简而言之:这意味着它保存原始值和更新的值,以便稍后更新它们。它处理其他事情;-)

对于使用代理EF/Linq2SQL类的人,我的解决方案是简单地删除我的子实体上的父引用。

因此,在我的模型中,我选择了关系,并将Parent引用更改为Internal而不是Public。

可能不是一个理想的解决方案,但对我有效。

可以删除virtual关键字:

public virtual Patient Patient { get; set; } -> public Patient Patient { get; set; }

请记住,当您删除virtual关键字时,延迟加载将被关闭。

我可以使用下面描述的方法来解决这个问题:

http://mytechworld.officeacuity.com/index.php/2010/02/serializing-entity-framework-objects-into-json-using-asp-net-mvc/