Tomas Aschan
Tomas Aschan

Reputation: 60664

JSON-serialization of NHibernate entities using the builtin JsonResult in ASP.NET MVC yields circular dependency error

I'm trying to return a JSON list of stuff from my server via an ASP.NET MVC front layer:

var stuff = repo.GetStuff();
return Json(stuff);

However, instead of the expected JSON, I get an error message stating

A circular reference was detected while serializing an object of type 'System.Reflection.RuntimeModule'.

I think I've found where this happens, but to explain it I need a simple example domain model as follows:

enter image description here

I am (lazily?) loading a selection of documents from NHibernate, like so:

var session = getNHibernateSession();
var query = new NhQueryable<Document>(session.GetSessionImplementation());
var docs = query.ToList().AsEnumerable();

I then pass the documents to return a JsonResult in my controller:

return Json(docs, JsonRequestBehavior.AllowGet);

Now, when Json() serailizes the collection, it walks over the properties of a document, finds a person. It serializes that person, and finds a project. It serializes the project, and finds - that's right - the person again! Since I'm lazy loading, it can just keep walking for ever if nothing stops it, but it's stopped by a circular reference error.

I don't really need to go all these levels down (I'd be fine without loading the project in the first place) - can I somehow affect how Json() serializes this collection, to not go further than, say, 2 levels down? I've googled around a little, but most of what I find seems to be from people who decided to use a serializing library directly, rather than just using the built-in functionality in .NET MVC. (Note: The solution to this problem must be possible to apply specifically to this case, since I might want to get JSON lists of people, including projects, somewhere else in the application...)

Upvotes: 3

Views: 2156

Answers (3)

Arkadas Kilic
Arkadas Kilic

Reputation: 2484

To preserve object references in JSON, add the following code to Application_Start method in the Global.asax file:

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.All;

Upvotes: 0

Ivo
Ivo

Reputation: 8362

If you are retrieving Json, you have a service api. You have to design the api besides the implementation. Does the page that will be using it need all those fields and collections? probably not. What about adding more properties for other features and services? They will start appear in all the requests.

What you need is to use a ViewModel or just an anonymous type with the desired structure:

var session = getNHibernateSession();
var query = new NhQueryable<Document>(session.GetSessionImplementation());
var docs = query.ToList();
var result = query.Select(x => new {
                                       x.Id,
                                       x.Name,
                                       People = new { p.Id, 
                                                      p.Name, 
                                                      p.Title
                                                    }
                                    });

return Json( result, JsonRequestBehavior.AllowGet);

This way you can control what is being rendered and how.

Upvotes: 1

Brian Ball
Brian Ball

Reputation: 12606

It's already been answered here.

Also, it's generally a bad idea to expose your domain entities like this. If it's for read-only purposes it might not be so bad, but if any of your action methods accept a domain entity, then a specifically formatted request can overwrite properties on your domain entity that you don't want to (such as your PK).

Upvotes: 0

Related Questions