将对象序列化为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; } }
- 忽略Json.net中的空字段
- 任务不是可序列化的:java.io.NotSerializableException仅在类不是对象时调用函数外的函数
- Java的 – 我什么时候必须改变serialVersionUID?
- 在JavaScript中序列化/反序列化对象的最佳方法?
- JSON.NET错误检测到types的自我引用循环
- 使用json.net反序列化没有types信息的多态json类
- Ruby对象和JSON序列化(不含Rails)
- 为什么DateTime.MinValue不能在UTC之前的时区中序列化?
- 为什么当一个构造函数用@JsonCreator注解时,它的参数必须用@JsonProperty注解?