Recently, I’ve been playing a lot with ASP.NET Web API, as it is really an exciting technology. It works exceptionally well with JSON.NET (Henrik Nielsen wrote a great post on how to return JSON from ASP.NET Web API using JSON.NET).

However, as it’s been the case in the past with i.e. WCF, I’ve run into a issue with serializing Entity Framework object to JSON when returning them from the ApiController. This is not, by any means, an issue in ASP.NET Web API, as the problem in this case lies in the EF lazy loading, which causes circular reference between the objects. This then throws a corresponding error – “A circular reference was detected while serializing an object of type (…)” if you are using JavaScriptSerializer, “Self referencing loop detected for type (…)” if you are using JSON.NET and so on.

I thought it might be useful to remind about possible solutions to this. There is an easy workaround.

More after the jump.

Our models & errors

For starters let’s take a look at our two simple models.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

publicclassUrl

{

publicintUrlId{get;set;}

publicstringAddress{get;set;}

publicstringDescription{get;set;}

publicstringUid{get;set;}

publicvirtualList<Tag>Tags{get;set;}

}

publicclassTag

{

publicintTagId{get;set;}

publicstringName{get;set;}

}

And the DbContext.

C#

1

2

3

4

5

publicclassUrlzipContext:DbContext

{

publicDbSet<Url>Urls{get;set;}

publicDbSet<Tag>Tags{get;set;}

}

Finally, here is our ApiController.

C#

1

2

3

4

5

6

7

8

9

10

publicclassValuesController:ApiController

{

DbContext db=newDbContext();

// GET /api/values

publicList<Url>Get()

{

varurls=db.Urls.Include("Tags").ToList();

returnurls;

}

}

If we try to return a simple Url from the ApiController, using the example above, we’d get the following error (note, the error comes from JSON.NET, but any other serializer would throw similar.

Solution – data projection

Data projection – in other words, passing to serializer only a selected set of values, in a form of an object different from Domain Model Object. DTO is a massive topic on its own, so I will not discuss it here. In case you are interested, there is a great article on that here. In short, let’s just say that many people consider using DTO these days a very poor approach, while others firmly believe that it is abolutely necessary to use DTO instead of trying to serialize your Domain Model Objects. Personally, I am a data projection advocate as to me it only makes sense to NOT expose domain models to the View.

Anyway, moving aside from the discussion, we could use data projection to an anonymous type in the LINQ query and serialize that. In other words, that would act kind of like a ViewModel.

C#

1

2

varurls=db.Urls.Include("Tags").Select(i=>

new{i.Uid,i.UrlId,i.Address,i.Description,i.Tags});

This still wouldn’t compile as our old Get() method was returning a List, whereas in here we are dealing with IQueryable of Anonymous Type. Unfortunately there is no simple way to return IQueryable of Anonymous Type or IEnumerable of Anonymous Type from an ApiController method. So we’ll use a bit of hack and change the method signature to:

C#

1

publicdynamicGet()

This is C# 4.0+ only, but we are talking ASP.NET Web API here, aren’t we? So the whole thing now looks like this:

C#

1

2

3

4

5

6

publicdynamicGet()

{

varurls=db.Urls.Include("Tags").Select(i=>

new{i.Uid,i.UrlId,i.Address,i.Description,i.Tags});

returnurls;

}

Of course in this case we lose the type safety, but again, it produces the expected result.

Summary

If you run into problems with your EF objects, do not worry, there are ways to work around them. I’ve found this solution useful when experimenting with ASP.NET Web API, especially with much more complex entities that the ones used here as an example. If you don’t entirely agree, instead of returning anonymous object, you can also return a custom type, and effectively have a ViewModel.

FYI, you can also disable Lazy Loading in the constructor of the dbContext… I.E.
public DbContext(){
Configuration.LazyLoadingEnabled = false;
}
Then you won’t have to do it every time you need to use your DbContext.

mycall

It should be noted that the use of dynamic might not work in medium trust mode.

http://aquabirdconsulting.com Khalid Abuhakmeh

Projection makes the most sense to me, since there might be sensitive / internal data on your models that doesn’t make sense to expose in an API.

Seems like a better rule of thumb, considering your application could change over time while people could be dependent on or API staying the same.