37

正如这篇文章中提到的,我在序列化实体框架代理时遇到 Json 序列化错误:

序列化“System.Data.Entity.DynamicProxies.PurchaseOrder_446B939192F161CDBC740067F174F7A6059B0F9C0EEE68CD3EBBD63CF9AF5BD0”类型的对象时检测到循环引用。

但不同的是,我的实体中没有循环引用,它只发生在我们的生产环境中。本地一切正常...

我的实体:

public interface IEntity
{
    Guid UniqueId { get; }
    int Id { get; }
} 

public class Entity : IEntity
{
    public int Id { get; set; }
    public Guid UniqueId { get; set; }
}

public class PurchaseOrder : Entity
{
    public string Username { get; set; }
    public string Company { get; set; }

    public string SupplierId { get; set; }
    public string SupplierName { get; set; }

    public virtual ICollection<PurchaseOrderLine> Lines { get; set; }
}

public class PurchaseOrderLine : Entity
{
    public string Code { get; set; }
    public string Name { get; set; }
    public decimal Quantity { get; set; }
}

我的 PurchaseOrderController 上的 GetCurrent 操作引发异常:

public class PurchaseOrderController : Controller
{
    private readonly IUnitOfWork _unitOfWork;

    public PurchaseOrderController(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public JsonResult GetCurrent()
    {
        return Json(EnsurePurchaseOrder(), JsonRequestBehavior.AllowGet);
    }

    private PurchaseOrder EnsurePurchaseOrder()
    {
        var company = RouteData.GetRequiredString("company");
        var repository = _unitOfWork.GetRepository<PurchaseOrder>();

        var purchaseOrder = repository
                .Include(p => p.Lines)
                .FirstOrDefault
                (
                    p => p.Company == company && 
                         p.Username == User.Identity.Name
                );

        if (purchaseOrder == null)
        {
            purchaseOrder = repository.Create();
            purchaseOrder.UniqueId = Guid.NewGuid();
            purchaseOrder.Company = company;
            purchaseOrder.Username = User.Identity.Name;
            _unitOfWork.SaveChanges();
        }

        return purchaseOrder;
    }
}
4

9 回答 9

44

选项 1(推荐)

尝试关闭 DbContext 上的代理对象创建

DbContext.Configuration.ProxyCreationEnabled = false;

通常这种情况是因为应用程序正在使用 POCO 对象(T4 生成或代码优先)。当实体框架想要跟踪未内置于 POCO 对象中的对象中的更改时,就会出现问题。为了解决这个问题,EF 创建了缺少 POCO 对象中的属性且不可序列化的代理对象。

我推荐这种方法的原因;使用网站意味着您可能不需要对实体框架对象进行更改跟踪(有状态),它释放了内存和 cpu,因为更改跟踪被禁用,并且它将以相同的方式在所有对象上一致地工作。

选项 2

使用允许自定义序列化对象的序列化程序(如 ASP.Net 4 中已包含的JSON.Net )。

我不推荐这种方法的原因是最终自定义对象序列化逻辑将需要将代理对象序列化为其他对象类型。这意味着您依赖逻辑来向下游传递结果。更改对象意味着更改逻辑,并且在 ASP.Net MVC 项目(任何版本)中,除了更改视图之外,您还需要更改一些其他内容,这些内容在首先编写逻辑的人之外并不容易知道。

选项 3(实体框架 5.x +)

使用.AsNoTracking()将禁用特定查询的代理对象。如果您需要使用更改跟踪,这可以为解决方案 #1 提供一个很好的中间解决方案。

于 2013-06-06T16:57:07.790 回答
30

您的 POCO 实体是完全可序列化的。您的问题是 EF 运行时为您创建的动态代理通常不是。您可以将其设置为context.Configuration.ProxyCreationEnabledfalse但随后您将失去延迟加载。我强烈建议您使用Json.NET支持 EF 实体的序列化:

ADO.NET 实体框架支持意外添加到 Json.NET

适用于 .NET 的流行高性能 JSON 框架

于 2013-06-06T05:29:55.370 回答
13

我花了无数个小时尝试我发现分散在整个网络中的所有各种解决方案,包括:

  • [Json忽略]
  • 内部吸气剂
  • 禁用 LazyLoadingEnabled 和 ProxyCreationEnabled
  • 将 ReferenceLoopHandling 设置为“忽略”
  • 在需要的地方小心使用显式加载

所有这些最终对我来说都是徒劳的。忽略一个属性帮助了一个查询,但伤害了另外 3 个。感觉就像编程相当于打地鼠。

我的问题的背景是从我的应用程序中输入的数据必须是 JSON。没有办法解决它。插入和更新显然不会造成什么问题。但是选择存储在规范化数据库中的数据(在我的例子中包括版本历史)进行序列化是一场噩梦。

解决方案:

将您需要的数据(属性)作为匿名对象返回。

代码示例:

在这种情况下,我需要 3 张最新票,基于“预定日期”。但还需要存储在相关实体中的几个属性。

var tickets =
     context.TicketDetails
    .Where(t => t.DateScheduled >= DateTime.Now)
    .OrderBy(t => t.DateScheduled)
    .Take(3)
    .Include(t => t.Ticket)
    .Include(t => t.Ticket.Feature)
    .Include(t => t.Ticket.Feature.Property)
    .AsEnumerable()
    .Select(
        t =>
        new {
            ID = t.Ticket.ID,
            Address = t.Ticket.Feature.Property.Address,
            Subject = t.Ticket.Subject,
            DateScheduled = String.Format("{0:MMMM dd, yyyy}", t.DateScheduled)
    }
);

瞧,没有自引用循环。

我意识到,鉴于实体和对象可能会发生变化,这种情况可能并不适用于所有情况。但是,如果所有其他方法都失败了,那当然值得考虑。

于 2015-03-20T21:34:59.270 回答
2

无论类有其他类的引用,只需添加这样的属性

[Newtonsoft.Json.JsonIgnoreAttribute]
public virtual ICollection<PurchaseOrderLine> Lines { get; set; }

现在一切顺利

于 2014-11-17T15:55:05.027 回答
2

我遇到了同样的问题,在我的情况下,我所做的只是传递了只需要查看的列。只有2。

 List<SubCategory> lstSubCategory = GetSubCateroy() // list from repo

 var subCategoryToReturn = lstSubCategory.Select(S => new { Id  = S.Id, Name = S.Name }); 

return this.Json(subCategoryToReturn , JsonRequestBehavior.AllowGet);
于 2016-03-26T11:29:04.010 回答
1

我遇到了同样的错误,但是我在生产服务器和本地都看到了它。更改 DbContext 配置并不能完全解决我的问题。向我提出了一个不同的解决方案

[IgnoreDataMember]

数据库实体引用的属性。如果这听起来与您的问题更相关,请参阅此处的帖子。

ASP.NET Web API 序列化 JSON 错误:“自引用循环”

于 2014-06-02T17:48:26.227 回答
1

在你的DbContext类中,添加这行代码:

this.Configuration.ProxyCreationEnabled = false;

例如:

public partial class EmpDBEntities : DbContext
{
    public EmpDBEntities()
        : base("name=EmpDBEntities")
    {
        this.Configuration.ProxyCreationEnabled = false;
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        throw new UnintentionalCodeFirstException();
    }

    public virtual DbSet<Department> Departments { get; set; }
    public virtual DbSet<Employee> Employees { get; set; }
}
于 2017-05-25T23:04:04.780 回答
0

The circular reference happens because you use eager loading on the object.

You have 3 methods:

  • Turn off eager loading when your loading your Query (linq or lambda) DbContext.Configuration.ProxyCreationEnabled = false;
  • Detach the objects (= no eager loading functionality & no proxy)
    • Repository.Detach(entityObject)
    • DbContext.Entry(entityObject).EntityState = EntityState.Detached
  • Clone the properties
    • You could use something like AutoMapper to clone the object, don't use the ICloneable interface, because it also clones the ProxyProperties in the object, so that won't work.
  • In case you are building an API, try using a separte project with a different configuration (that doesn't return proxies)

PS. Proxies is the object that's created by EF when you load it from the Entity Framework. In short: It means that it holds the original values and updated values so they can be updated later. It handles other things to ;-)

于 2014-03-04T11:35:07.403 回答
0

我遇到了同样的问题,并通过在参考管理器的项目扩展中取消选中 Json.NET 来解决它。

(参见图片http://i.stack.imgur.com/RqbXZ.png

我还必须更改 project.csproj 文件以映射新版本的正确路径:

<Reference Include="Newtonsoft.Json">
  <HintPath>..\packages\Newtonsoft.Json.6.0.5\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>

仍然需要配置 web.config

  <dependentAssembly>
    <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
  </dependentAssembly>

请注意,在 web.config 文件中,尽管安装的版本是 6.0.5,但我被迫引用 OLDER (6.0.0.0) 版本。

希望能帮助到你!

于 2014-09-23T18:14:38.530 回答