将对象序列化为JSON时,循环引用检测到exception

就像在这篇文章中提到的,我在序列化entity framework代理的时候遇到了一个Json序列化错误:

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

但是区别在于,我的实体中没有循环引用, 只有在我们的生产环境中才会出现。 本地一切工作正常…

我的实体:

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操作抛出exception:

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

你的POCO实体是完美的序列化。 你的问题是,EF运行时为你创build的dynamic代理通常不是。 您可以将context.Configuration.ProxyCreationEnabled设置为false但是您将丢失延迟加载。 我强烈build议您使用支持序列化EF实体的Json.NET

ADO.NETentity framework支持意外添加到Json.NET

stream行的.NET高性能JSON框架

选项1(推荐)

尝试closuresDbContext上的代理对象创build 。

 DbContext.Configuration.ProxyCreationEnabled = false; 

通常这种情况是因为应用程序正在使用POCO对象(T4生成或Code-First)。 当entity framework想要跟踪你的对象中没有内置到POCO对象中的变化时,就会出现这个问题。 为了解决这个问题,EF创build了缺lessPOCO对象中的属性的代理对象,并且不可序列化。

我推荐这种方法的原因; 使用网站意味着您可能不需要在Entity Framework对象上进行更改跟踪(有状态),它可以释放内存和CPU,因为更改跟踪被禁用,它将以同样的方式在所有对象上始终如一地工作。

选项2

使用一个序列化程序(如已经包含在ASP.Net 4中的JSON.Net ),允许定制序列化对象。

我不推荐这种方法的原因是,最终定制对象序列化逻辑将需要串行代理对象作为其他对象types。 这意味着你有一个逻辑的依赖,以提供下游的结果。 改变对象意味着改变逻辑,在一个ASP.Net MVC项目(任何版本)中,而不是只改变一个视图,你还有别的东西可以改变,这是谁不知道谁先写逻辑。

选项3(entity framework5.x +)

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

我花了无数个小时尝试所有在网上发现的各种解决scheme,其中包括:

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

所有这些最终都certificate对我来说毫无结果。 忽略财产帮助了一个查询,但伤害了另外3个。 感觉就像编程相当于重击一样。

我的问题的上下文是数据进入我的应用程序必须是JSON。 没有办法绕过它。 插入和更新明显地造成更less的问题。 但select存储在规范化数据库中的数据(在我的情况下,包括版本历史logging)被序列化是一场噩梦。

解决scheme:

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

一个代码示例:

在这种情况下,我需要基于“预定date”的最新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) } ); 

瞧,没有自我引用循环。

我认识到,在所有情况下,由于实体和对象可能发生变化,这种情况可能是不够的。 但是,如果一切都失败了,肯定值得考虑一下。

无论什么类的其他类的引用只是像这样添加属性

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

现在一切顺利

循环引用发生是因为您使用对象上的急切加载。

你有3种方法:

  • 当您加载您的查询(linq或lambda)时closures加载加载DbContext.Configuration.ProxyCreationEnabled = false;
  • 分离对象(=没有渴望的加载function和没有代理)
    • Repository.Detach(entityObject)
    • DbContext.Entry(entityObject).EntityState = EntityState.Detached
  • 克隆属性
    • 你可以使用像AutoMapper这样的东西来克隆对象,不要使用ICloneable接口,因为它也克隆了对象中的ProxyProperties,所以这是行不通的。
  • 如果您正在构buildAPI,请尝试使用不同configuration的separte项目(不返回代理)

PS。 代理是EF从entity framework加载它时创build的对象。 简而言之:这意味着它保存了原始值和更新值,以便稍后可以更新。 它处理其他的事情;-)

我有同样的错误,但是我看到它在生产服务器和本地。 更改DbContextconfiguration并不完全解决我的问题。 一个不同的解决scheme是提出给我的

 [IgnoreDataMember] 

属性DB实体引用。 看到这里的post,如果这听起来更适合你的问题。

ASP.NET Web API序列化JSON错误:“自我参考循环”

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

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

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

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

仍然不得不configurationweb.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文件中,我被强制引用了OLDER(6.0.0.0)版本,虽然安装的版本是6.0.5。

希望它有帮助!

我有同样的问题,我所做的是通过只需要列来查看,在我的情况。 只有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); 

在您的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; } }