How to use Delta<T> from Microsoft ASP.NET Web API OData with Code First\JsonMediaTypeFormatter
Asked Answered
H

2

9

What is the issue?

I am trying to enable patching in my ASP.net web api app. I'm using code first entity framework.

I have the following method header which I can set a breakpoint in and it will hit:

[AcceptVerbs("PATCH")]
public async Task<HttpResponseMessage> Patch(long appId, long id, Delta<SimpleFormGroup> formGroup)

However when I call formGroup.Patch(entity), no changes are made to my entity. If I put the following into the immediate window:

formGroup.GetChangedPropertyNames()

Then this collection is empty, which seems wrong.

What have I tried?

I have been referring to the following examples

http://techbrij.com/http-patch-request-asp-net-webapi http://www.strathweb.com/2013/01/easy-asp-net-web-api-resource-updates-with-delta/

It seems to be a problem with the Json MediaType Formatter not knowing how to build the Delta object correctly, however in the 2nd link filip does seem to suggest that it should work without the oDataMediaTypeFormatter.

I have started down the line of trying to serialise my model to EDMX representation, then from there extract the CSDL so I can create an oDataMediaTypeFormatter, but I have hit a snag there too, plus it seems a bit overkill.

If anyone could shed any light on this it'd be much appreciated. Let me know if any more information is needed.

EDIT:

Here is the class definition for SimpleFormGroup:

public class SimpleFormGroup
{
    public int LastUpdate;

    public string Identifier;

    public string Title;

    public int DisplayOrder;
}

And here is the data that I am sending:

Content-Type: 'application/json'

{ "DisplayOrder" : "20 }
Holliman answered 6/2, 2013 at 12:35 Comment(4)
Can you add the class definition for SimpleFormGroup and the JSON you're sending in the request to PATCH?Steffens
See above... I have actually found a workaround for this for the moment but I would be interested to hear your thoughts tooHolliman
That is not valid JSON, considering the quote to the left of the 20. Is there a closing quote around the value or no opening quote?Atrium
I wonder what changed between webapi 1 and webapi 2. Delta used to work with JSON.NET just fine in WebAPI 1 using Filip's Delta codeVomiturition
S
9

Interesting, it looks like Delta<T> with int members doesn't work in JSON.

Unfortunately, Delta<T> was created specifically for OData. If Delta<T> appears to be working with any formatter other than OData, it's a coincidence rather than being intentional.

The good news though is that there's nothing stopping you from defining your own PATCH format for JSON, and I'd be surprised if no one has already written one that works better with Json.NET. It's possible that we'll revisit patching in a future release of Web API and try to come up with a consistent story that works across formatters.

Steffens answered 6/2, 2013 at 16:42 Comment(4)
having a supportable Delta<T> for use not only from OData but from Web API in general would be extremely beneficial. it saddens me each time I create a Patch() action that relies on discrete knowledge of the submitter to perform a partial update :( is there an issue on codeplex we can vote on to help legitimize the need?Equilibrant
I noticed it chokes on int and Guid for JSON. I posted a workaround here strathweb.com/2013/01/…. In short, JSON.NET would treat int as long and Guid as string, so the Delta<T> code needs to shield against that,Mistranslate
I wish they would stop lolly gagging around on aspnetwebstack.codeplex.com/workitem/777 .. This would be so useful for mvc / web api in general.Wicketkeeper
Has anyone gotten this to work with json.net and webapi2? I've used filipw's Delta code in a WebAPI 1 project and it worked with json.net 4.5.11Vomiturition
H
4

Thanks to Youssef for investigating and discovering why things weren't working. Hopefully that can get solved down the line.

I managed to crack this myself in the end after poring over the oData package source. I chose to implement another MediaTypeFormatter that wraps up the logic as it provides easy access tio HttpContent, but there are other ways to achieve this.

The key part was figuring out how to interpret the code first model, see the commented line below:

public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
{
    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; 
}

Hope this saves someone some trouble!

Holliman answered 8/2, 2013 at 9:12 Comment(1)
Where do you put this code?Hackworth

© 2022 - 2024 — McMap. All rights reserved.