C# 将对象序列化为 JSON 时循环引用检测到异常
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16949520/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Circular reference detected exception while serializing object to JSON
提问by Mathijs
Just as mentioned in thispost, I am getting a Json serialization error while serializing an Entity Framework Proxy:
正如这篇文章中提到的,我在序列化实体框架代理时遇到了 Json 序列化错误:
A circular reference was detected while serializing an object of type 'System.Data.Entity.DynamicProxies.PurchaseOrder_446B939192F161CDBC740067F174F7A6059B0F9C0EEE68CD3EBBD63CF9AF5BD0'.
在序列化类型为“System.Data.Entity.DynamicProxies.PurchaseOrder_446B939192F161CDBC740067F174F7A6059B0F9C0EEE68CD3EBBD63CF9AF5BD0”的对象时检测到循环引用。
But the difference is, I don'thave a circular reference in my entities, and it onlyoccurs in our production environment. Locally everything works fine...
但不同的是,我的实体中没有循环引用,它只发生在我们的生产环境中。在本地一切正常...
My Entities:
我的实体:
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; }
}
The GetCurrent action on my PurchaseOrderController throwing the exception:
我的 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;
}
}
采纳答案by Radu Popovici - Oncica
Your POCO entities are perfectly serializable. Your problem is that the dynamic proxies the EF runtime creates for you are usually not. You can set the context.Configuration.ProxyCreationEnabledto falsebut then you lose lazy loading. My strong recommendation to you is to use Json.NETwhich supports serialization for EF entities:
您的 POCO 实体是完全可序列化的。您的问题是 EF 运行时为您创建的动态代理通常不是。您可以将 设置为context.Configuration.ProxyCreationEnabled,false但是您会丢失延迟加载。我强烈建议您使用Json.NET它支持 EF 实体的序列化:
ADO.NET Entity Framework support accidently added to Json.NET
ADO.NET Entity Framework 支持意外添加到 Json.NET
回答by Erik Philips
Option 1 (recommended)
选项 1(推荐)
Try turning off Proxy object creation on your DbContext.
DbContext.Configuration.ProxyCreationEnabled = false;
Typically this scenario is because the application is using POCO objects (Either T4 Generated or Code-First). The problem arises when Entity Framework wants to track changes in your object which is not built into POCO objects. To resolve this, EF creates proxy objects which lack the attributes in the POCO objects, and aren't serializable.
通常这种情况是因为应用程序正在使用 POCO 对象(T4 生成或代码优先)。当实体框架想要跟踪未内置到 POCO 对象中的对象中的更改时,就会出现问题。为了解决这个问题,EF 创建了缺少 POCO 对象中的属性且不可序列化的代理对象。
The reasons why I recommend this approach; using a website means that you probably don't need change tracking (stateful) on Entity Framework objects, it free's up memory and cpu because change tracking is disabled and it will work consistantly on all your objects the same way.
我推荐这种方法的原因;使用网站意味着您可能不需要对 Entity Framework 对象进行更改跟踪(有状态),它释放内存和 CPU,因为更改跟踪已禁用,并且它将以相同的方式在所有对象上一致地工作。
Option 2
选项 2
Use a serializer (like JSON.Netwhich is already included in ASP.Net 4) that allows customization to serialize the object(s).
使用允许自定义序列化对象的序列化程序(如ASP.Net 4 中已包含的JSON.Net)。
The reasons I do not recommend this approach is that eventually custom object serialization logic will be need to serial proxy objects as otherobjects types. This means you have a dependency on logic to deliver a result downstream. Changing the object means changing logic, and in an ASP.Net MVC project (any version) instead of only changing a View you have some thing else to change that is not readily known outside of whoever wrote the logic first.
我不推荐这种方法的原因是,最终自定义对象序列化逻辑将需要将代理对象序列化为其他对象类型。这意味着您依赖于向下游交付结果的逻辑。更改对象意味着更改逻辑,并且在 ASP.Net MVC 项目(任何版本)中,而不是仅更改视图,您还需要更改一些其他东西,这些东西在首先编写逻辑的人之外并不容易知道。
Option 3 (Entity Framework 5.x +)
选项 3(实体框架 5.x +)
Use .AsNoTracking()which will disable the proxy objects on the specific query. If you need to use change tracking, this allows a nice intermediate solution to solution #1.
使用.AsNoTracking()将禁用特定查询上的代理对象。如果您需要使用更改跟踪,这为解决方案 #1 提供了一个很好的中间解决方案。
回答by NicoJuicy
The circular reference happens because you use eager loading on the object.
循环引用的发生是因为您在对象上使用了预先加载。
You have 3 methods:
你有3种方法:
- 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)
- 加载查询(linq 或 lambda)时关闭预先加载 DbContext.Configuration.ProxyCreationEnabled = false;
- 分离对象(= 没有预先加载功能和没有代理)
- Repository.Detach(entityObject)
- DbContext.Entry(entityObject).EntityState = EntityState.Detached
- 克隆属性
- 您可以使用 AutoMapper 之类的东西来克隆对象,不要使用 ICloneable 接口,因为它还会克隆对象中的 ProxyProperties,因此无法正常工作。
- 如果您正在构建 API,请尝试使用具有不同配置的单独项目(不返回代理)
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 ;-)
附注。代理是当您从实体框架加载它时由 EF 创建的对象。简而言之:这意味着它保存了原始值和更新后的值,以便以后可以更新。它处理其他事情;-)
回答by Freestyle076
I had the same error, however I saw it both on production server and locally. Changing the DbContext configuration didn't quite solve my issue. A different solution was presented to me with the
我有同样的错误,但是我在生产服务器和本地都看到了它。更改 DbContext 配置并没有完全解决我的问题。向我提供了一个不同的解决方案
[IgnoreDataMember]
attribute on DB entity references. See the post here if this sounds more pertinent to your issue.
数据库实体引用上的属性。如果这听起来与您的问题更相关,请参阅此处的帖子。
ASP.NET Web API Serialized JSON Error: "Self Referencing loop"
回答by Marco
I had the same problem and resolved it by un-checking Json.NET in the project Extensions in the Reference Manager.
我遇到了同样的问题,并通过在参考管理器的项目扩展中取消选中 Json.NET 来解决它。
(see the image http://i.stack.imgur.com/RqbXZ.png)
(见图片http://i.stack.imgur.com/RqbXZ.png)
I also had to change the project.csproj file to map the correct path for the new version:
我还必须更改 project.csproj 文件以映射新版本的正确路径:
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.6.0.5\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
and still had to configure the web.config
并且仍然必须配置 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>
Note that in the web.config file I was forced to refer to the OLDER (6.0.0.0) version though the installed version was 6.0.5.
请注意,在 web.config 文件中,尽管安装的版本是 6.0.5,但我还是被迫参考了 OLDER (6.0.0.0) 版本。
Hope it helps!
希望能帮助到你!
回答by Ali Adravi
Whatever classes have the reference of other class just add attribute like this
无论类有其他类的引用,只需添加这样的属性
[Newtonsoft.Json.JsonIgnoreAttribute]
public virtual ICollection<PurchaseOrderLine> Lines { get; set; }
Now everything work smooth
现在一切顺利
回答by pimbrouwers
I spent countless hours attempting all of the various solutions I found scattered throughout the web, including:
我花了无数个小时尝试我发现分散在整个网络中的所有各种解决方案,包括:
- [JsonIgnore]
- Internal Getters
- Disabling LazyLoadingEnabled and ProxyCreationEnabled
- Setting ReferenceLoopHandling to "ignore"
- Carefully using explicit loading where needed
- [Json忽略]
- 内部吸气剂
- 禁用 LazyLoadingEnabled 和 ProxyCreationEnabled
- 将 ReferenceLoopHandling 设置为“忽略”
- 在需要的地方小心地使用显式加载
All of which ultimately proved fruitless for me. Ignoring a property helped one query, but hurt 3 others. It felt like the programming equivalent to whack-a-mole.
所有这些最终对我来说都是徒劳的。忽略属性帮助了一个查询,但伤害了另外 3 个。感觉就像编程相当于打地鼠。
The context of my problem was that the data going in an out of my application had to be JSON. No way around it. Inserts and updates obviously pose much less of a problem. But selecting data that's stored in a normalized database (and in my case including a version history) to be serialized is a nightmare.
我的问题的背景是,从我的应用程序中传入的数据必须是 JSON。没办法。插入和更新显然不是问题。但是选择存储在规范化数据库中的数据(在我的情况下包括版本历史记录)进行序列化是一场噩梦。
The solution:
解决方案:
Return the data (properties) you need as anonymous objects.
将您需要的数据(属性)作为匿名对象返回。
A code example:
一个代码示例:
In this case I needed the 3 latest tickets, based on "Date Scheduled". But also needed several properties stored in related entities.
在这种情况下,我需要 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)
}
);
And voila, no self referencing loops.
瞧,没有自引用循环。
I realize this situation may not be adequate in all cases given that entities and objects may change. But it's certainly worth some consideration if all else fails.
我意识到考虑到实体和对象可能会发生变化,这种情况可能并不适用于所有情况。但如果一切都失败了,这当然值得考虑。
回答by BJ Patel
I was having the same issue, what I have done is have passed only needed column to view , In my case. only 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);
回答by Abdi
In your DbContextclass, add this line of code:
在您的DbContext课程中,添加以下代码行:
this.Configuration.ProxyCreationEnabled = false;
For example:
例如:
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; }
}

