Resolving circular references when binding the MVC Grid

Monday, January 25, 2010 by ASP.NET MVC Team | Comments 14

Sometimes when performing ajax databinding with the MVC grid you may end up with unexpected result: server error 500.

image

What is even stranger is that server-side binding works as expected with the same data binding code. Let’s demystify this problem:

The problem

You will see the following  if you use FireBug or Fiddler to inspect the response:

image

The error message is “A circular reference was detected while serializing an object of type” followed by some type. If you google this up you will find out that this exception is thrown by the JavaScriptSerializer class because of circular references detected. “What the heck is a circular reference anyway?” you may ask. In my example it is the well known Orders-Customers relationship:

image

Most O/R mapping tools (it is Entity Framework in my case) would create reference properties. In this case the Orders object will have a Categories property and the Categories object will have an Orders prooperty:

image

Since the MVC grid is ajax-bound to Orders the JavaScriptSerializer will try to serialize the data. During that process it tries to serialize all properties of the Orders type which includes “Categories” as well. However when traversing the Categories object the JavaScriptSerializer discovers the circular reference (the Orders property) and gives up by throwing the aforementioned exception.

 

Solutions

There are three possible solutions of this problem:

  1. Use server binding (if this is an option at all).
  2. Avoid generating reference properties.
    If using EF you can change the visibility of the association property (make it anything but public):
     image
    In case of Linq To Sql you can prevent the property generation altogether:
    image
  3. Use ViewModel object instead of the original object created by the O/R mapping tool. This one is my favorite as it does not require any modifications to the model objects. The whole idea is explained in this blog post but I will cover it here as well:
    1. Create a new class which will contain only the properties required for databinding the grid (properties mapped to grid columns that is). If binding to EF make sure you add the key property (OrderID in this case) to the ViewModel even if you are not displaying it in the grid. Otherwise you will end up with NotSupportedException saying “Cannot find primitive type or property to sort by”.
    2. public class OrderViewModel
      {
      public int OrderID
      {
      get;
      set;
      }

      public DateTime? OrderDate
      {
      get;
      set;
      }
      }

    3. Modify your controller (or repository) to return object of the ViewModel type:
    4. public ActionResult Index()
      {
      var model = from o in new NorthwindEntities().Orders
      select new OrderViewModel
      {
      OrderID = o.OrderID,
      OrderDate = o.OrderDate
      };
      return View(model);
      }

      [GridAction]
      public ActionResult AjaxIndex()
      {
      var model = from o in new NorthwindEntities().Orders
      select new OrderViewModel
      {
      OrderID = o.OrderID,
      OrderDate = o.OrderDate
      };

      return View(new GridModel
      {
      Data = model
      });
      }

I hope this helps!

14 Comments

  • marco 07 Jun
    I have the problem in this row of code:
    <%= Html.Telerik().ScriptRegistrar() %>
    
    What can be?

    A circular reference was detected while serializing an object of type 'System.Data.Metadata.Edm.AssociationType'.

  • Daniel 01 Sep
    Excelent!

    It's work perfect!

    Tks, I use the ViewModel solution.
  • popnadrian 26 Oct
    Great, A good resource, I also have the idea no. 3 but not applied yet :)
  • Makoto 23 Nov
    With the ViewModel approach, won't you lose the built in Linq expression engine?

    Wouldn't we have to implement our own custom paging/sorting?

    That's what I'm running into at least. If I don't implement my own paging/sorting, then my application queries for EVERY record in the database.
  • Zare 29 Nov
    Hello,

    There is a simpler solution. You should use anonymous type when returning collection in GRIDACTION.

    Example


    [GridAction]
           
    public ActionResult _FirstLook()
           
    {
               
    return View(new GridModel(GetOrderDto()
        .Select(s=> new { OrderID = s.OrderID, ContactName = s.ContactName, ShipAddress =            s.ShipAddress, OrderDate = s.OrderDate } )
                ));

           
    }

    You should take care that Property names match those in real EntityObject which is bound to Grid.



     
  • Rino Batin 21 Dec
    Zare's solution works for me. Thanks!!!
  • mahmoud 19 Apr
    all of these solutions limit us! any better solution? i think it is not our problem!
  • Sam Critchley 06 May
    This works great. The inline projection is bit yuck though - AutoMapper fans have a read of this: 

    http://lostechies.com/jimmybogard/2011/02/09/autoprojecting-linq-queries/

    Not quite sure if the result is already enumerated though. Obviously wouldn't want that...
  • Travis 18 Jul
    The only real solution here is using a ViewModel which means I have more duplicate code that I have to create and maintain.  Lame.  Guess I won't use Telerik's grid if I'm using EF.
  • Stefan Dobrev 19 Jul
    Hi Travis,
    There are a number of options you can use in order to bring the creation and maintainability costs to a minimum. One of them is using AutoMapper to create those view models. Another one is using the MicroModels framework.
    Writing your own JavaScript serializer (instead of using the one in the framework which has this issue) is also an option, but I believe this will bring the maintainability cost to a whole new level.
    Hope this helps,
    sdobrev
  • Anil D 14 Sep
    Hi All,
    I m facing the same Circular Reference problem.
    In  my case i need to show associated Order in Customer Grid in a drop-down so order can be change when editing customer using Inline Edit mode.

    I can't use [ScriptIgnore] or ViewModel as this will remove column Order from Grid, which i need to show as well in the grid in drop-down list.

    So how can i prevent the circular refrence under this situation?
  • Bill 31 Oct
    This doesnt work for me.  when I use anonymous types, it barks at me that the "the model item passed into the dictionary is of type ...DbQuery...AnonymousType, but this dictionary requires a model item of type...IEnumerable [MyModelType]".
  • Robert Paddock 15 Nov
    I am using Telrik Open Access and I have run into the same problem.  What is the best work around for this.  
  • Jaime Pinheiro 10 Jan
    I solve the circular reference problem. I'm using EF 4.2 Code First and using Telerik Mvc Grid Paging, with ActionResult equals to new GridModel. I got this error while paging through ajax and I was wondering a way to perform an IQueryable<T>.Select<TResult> just before the serialization. I did this:
    Grid(Model)
        .RowAction(row => row.DataItem = new TModel {
             Prop = row.DataItem.Prop,
            ...
        })
    In that way I could keep data source as IQueryable, with default paging, sorting, grouping and filtering in Sql Queries.
    My problem is solved.

Add comment

  1. Formatting options
       
     
     
     
     
       
  2. (optional, emails won't be shown on public pages)
  3. (optional)