How to Deserialize Delta<T> from JSON using oDataMediaTypeFormatter with Entity Framework Code First
Today I was struggling trying to work out how I can make use of the oDataMediaTypeFormatter in ASP.Net Web API when you only have a code first model to work with. For the impatient, scroll to the bottom for the quick solution!
The Problem
I’d been reading various articles about oData, Particularly the following:
- http://blogs.msdn.com/b/alexj/archive/2012/08/15/odata-support-in-asp-net-web-api.aspx
- http://www.strathweb.com/2013/01/easy-asp-net-web-api-resource-updates-with-delta/
- http://techbrij.com/http-patch-request-asp-net-webapi
What I was actually trying to do was to implement partial updates to resources using the PATCH verb, and it all seemed really simple on paper. However when it came down to it, there were several problems:
- You can’t use the [FromData] attribute with a Delta<T> parameter as this throws an exception
- JsonMediaTypeFormatter doesn’t seem to be able to deserialise to Delta<T>, contrary to what the TechBrijj article suggests.
Given the above, I tried to follow the instructions for ‘Doing more oData’ from Alex J’s article hoping to make use of the oDataMediaTypeFormatter. From the article:
// Create the OData formatter and give it the model ODataMediaTypeFormatter odataFormatter = new ODataMediaTypeFormatter(model);
There were more issues though:
- Since the article was written, the constructor is for the media type formatter is no longer public so can’t be used.
- The example requires that you define all aspects of your model explicitly which can be extremely time consuming. Try as I might I just couldn’t find anywhere that told me how I could get the information from a code first model. This was the closest I got: http://social.msdn.microsoft.com/Forums/kn/adodotnetdataservices/thread/603e84b2-5a8c-44b7-99c8-ae45cd2376dc
At this point I did what any upstanding developer would do, I swore loudly and went to lunch.
The Solution
Isn’t it annoying when things turn out to be so simple? Poring over the oData package source I found out it’s actually very easy to instantiate the media type formatter. Additionally, it was also a one liner to interpret the code first model (see below)
For additional reasons not explained here, I chose to wrap everything up in another custom MediaTypeFormatter, but anywhere you have a reference to an HttpContent object would work fine too.
public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) { var serialiser = new JsonSerializer(); var builder = new ODataConventionModelBuilder(); // This line will allow you to interpret all the metadata from your code first model builder.EntitySet<EfContext>("EfContext"); var model = builder.GetEdmModel(); var odataFormatters = ODataMediaTypeFormatters.Create(model); var delta = content.ReadAsAsync(type, odataFormatters).Result; var tcs = new TaskCompletionSource<object>(); tcs.SetResult(delta); return tcs.Task; }
So there we go. Hopefully someone will find this useful and not lose a morning because of it! Thanks to @filip_woj for his assistance on this issue.
Pete
hi roysvork, is it work in odata v4?