In my last post, I talked about the new Web API controllers in MVC and showed how they work with simple data. In the real world, of course, I want them to work with my existing data, which uses Entity Framework. It turns out that this is far from straightforward (at least, in the beta).
Let’s start by trying to expose some standard EF data. When I began coding my personal website www.cocktailsrus.com I used model-first rather than code-first development. (At that point, my focus was on getting to know jQuery mobile, and I was not concerned with best practices in MVC; I have refactored it since). So let’s go back to basics and begin with an .edmx version of the simple data I used in my last post.
The code to return this object using Entity Framework and Web API is as follows (with the result first placed into a variable so I can more easily examine the return in the debugger):
When we try and access this in Internet Explorer, we get the following error message:
“The type ‘BeverageType’ cannot be serialized to JSON because its IsReference setting is ‘True’. The JSON format does not support references because there is no standardized format for representing references. To enable serialization, disable the IsReference setting on the type or an appropriate parent class of the type.”
Interestingly, if we use Firefox, it actually works–but here the response is formatted as XML. Still, that suggests we’re almost there. Let’s make it more interesting. Let’s add an Association to our .edmx file to make it more realistic.
And let’s add an Include to our Entity Framework code so that we return the Association along with our object:
Strangely, we get exactly the same results as before. Internet Explorer gives the same JSON error message. Firefox returns the same XML, without the association. Perhaps turning off LazyLoading will improve the Firefox return?
Sadly, it makes no difference. We get exactly the same error, and since there is no convenient way to change the IsReference setting in an .edmx, perhaps it’s time to switch to Code First Generation and see if we can’t return JSON properly to Internet Explorer.
Here are my types:
And here is my DbContext:
Let’s run it again and see what happens now… And yes–this time, we’ve managed to break both Internet Explorer and Firefox! Both get the following error message:
“You must write an attribute ‘type’='object’ after writing the attribute with local name ‘__type’. “
Now, this error is actually useful. It tells us we need to turn off proxy generation, so let’s tweak our DbContext. And we may as well turn of lazy loading here while we’re at it:
Now the Web API makes a valiant effort, sends through the beginning of a JSON version of the data to both IE and Firefox, and then gives up with an error message:
Here is the error message:
“System.Runtime.Serialization.SerializationException: Object graph for type ‘Beverage’ contains cycles and cannot be serialized if reference tracking is disabled.”
At this point, I imagine we’re all starting to get a little frustrated. I know I am. So, to cut a long story (and a lot of Web searching) short… it looks like the fundamental problem lies in the interaction between the Entity Framework and the DataContractSerializer used in the beta. This is supposed to be fixed by a change to the JSON.Net serializer when Web API goes live – but in the meantime, what to do?
There are two possible approaches (and I’ve placed the code for both online here):
Substitute the JSON.Net serializer in place of the default. For this, you need first to follow the helpful instructions in this blog post: http://blogs.msdn.com/b/henrikn/archive/2012/02/18/using-json-net-with-asp-net-web-api.aspx and then
- Add an instruction to the serializer to ignore self-referencing loops
- Add the new serializer in the Global.asax
Use projection. The suggestions online seem to fall into two camps:
- Use projection with anonymous objects.
- Use projection with Data Transfer Objects.
But if I did want to use projection, why would I want to:
- return anonymous objects when I have perfectly good POCO objects, or
- create new DTOs when I already have perfectly good POCO objects?
So I wondered what would happen if I used projection with the same POCO Code First objects that the serializer had choked on when I returned them directly…
And here is the result in Firefox/Firebug:
And in Internet Explorer/F12 Developer Tools
The Web API is going to be great, but it takes a little work if you’re using the beta. If you want to look at the code for either solution I outlined above, it’s available here. (The full project is 13MB, even as a zip, so I just put up the code: if you want it to run, you’ll need to copy the files into a VS11 project and download the various Nugets, as well as getting EF to generate the database for you).
In my next article, I’m going to take a look at the thorny issue of whether to return an IQueryable and—if you do choose to do so—how you can protect yourself against over-large queries.
For related information, check out this course from Learning Tree: Building Web Applications with ASP.NET MVC.