Circular reference detected exception while serializing object to JSON

83,166

Solution 1

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.ProxyCreationEnabled to false but then you lose lazy loading. My strong recommendation to you is to use Json.NET which supports serialization for EF entities:

ADO.NET Entity Framework support accidently added to Json.NET

Popular high-performance JSON framework for .NET

Solution 2

Option 1 (recommended)

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.

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.

Option 2

Use a serializer (like JSON.Net which is already included in ASP.Net 4) that allows customization to serialize the object(s).

The reasons I do not recommend this approach is that eventually custom object serialization logic will be need to serial proxy objects as other objects 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.

Option 3 (Entity Framework 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.

Solution 3

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

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.

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.

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.

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.

Solution 4

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

Solution 5

I was having the same issue, what I have done is have passed only needed column to view , In my case. only 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);
Share:
83,166
Mathijs
Author by

Mathijs

Updated on August 30, 2020

Comments

  • Mathijs
    Mathijs over 3 years

    Just as mentioned in this post, I am getting a Json serialization error while serializing an Entity Framework Proxy:

    A circular reference was detected while serializing an object of type 'System.Data.Entity.DynamicProxies.PurchaseOrder_446B939192F161CDBC740067F174F7A6059B0F9C0EEE68CD3EBBD63CF9AF5BD0'.

    But the difference is, I don't have a circular reference in my entities, and it only occurs 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:

    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;
        }
    }
    
  • Mathijs
    Mathijs almost 11 years
    I'll have a look. Thank you. What I don't understand is why it "works on my machine"
  • Mathijs
    Mathijs almost 11 years
    Implemented the JsonNetResult as describe here and it solved all my problems!
  • deltree
    deltree almost 11 years
    yay, 2 hours of searching for this little feature
  • Antonio Petricca
    Antonio Petricca over 10 years
    Mathijs, you posted the final solution to all my problems, thank you!
  • Mathijs
    Mathijs about 10 years
    Seems like my original link doesn't work anymore. This one does: james.newtonking.com/archive/2008/10/16/…
  • Freestyle076
    Freestyle076 almost 10 years
    when/where should this line be called? in the DbContext constructor?
  • Erik Philips
    Erik Philips almost 10 years
    It can be turned of in the constructor. If you are using EF5+, then I'd recommend using AsNoTracking() instead, and only when you want to serialize the result.
  • user192344
    user192344 over 9 years
    i cant serialize the object without turn off the lazy load, is it possible to serizlize with json convert but not to turn off lazy load
  • Bellash
    Bellash over 9 years
    This seems not to work in my project, with attribute in metadata
  • Freestyle076
    Freestyle076 over 9 years
    Can you provide more detail?
  • Bellash
    Bellash over 9 years
    I have added [IgnoreDataMember] to some properties but the circular reference exception remains
  • pim
    pim almost 9 years
    As a side note to my response, a more robust solution, is Data Transfer Objects.
  • Ryan Vettese
    Ryan Vettese almost 9 years
    Option 1 should be the #1 answer.
  • Chad
    Chad over 8 years
    AsNoTracking saved the day!
  • Chris - Haddox Technologies
    Chris - Haddox Technologies almost 8 years
    Setting: DbContext.Configuration.ProxyCreationEnabled = false; works for me as I am only displaying data, not needing to track any changes.
  • razblack
    razblack almost 8 years
    this worked for me with EF 7 where needed to add foreign key reference objects for child collections.
  • Pedro Simões
    Pedro Simões over 7 years
    Nice and elegant solution ;)
  • John Babb
    John Babb over 7 years
    JsonNetResult worked for me as well. Linked changed: here
  • Orlando
    Orlando almost 7 years
    Thanks for this answer. Helped a lot :)
  • Nick.McDermaid
    Nick.McDermaid almost 7 years
    Yes! AsNoTracking gave me another error. I was pretty sure the circular reference was due to a dodgy relationship (navigation?) that EF put into the autogenerated class, and I think without an explicit select list it was trying to load thisnavigation. So my challenge as usual was finding the correct LINQ syntax to select a limited list. Thanks! Once I selected a limited list my error went away
  • daniel.caspers
    daniel.caspers over 6 years
    Thanks for option 3. Very helpful for serializing complex read only queries!
  • jonesy827
    jonesy827 about 3 years
    Just FYI for anyone reading, with this solution you lose lazy loading.