10

From reading the RFC specification of the Patch verb it's clear that the Patch verb shouldn't get values to partially update the entity but operations to make:

...With PATCH, however, the enclosed entity contains a set of instructions describing how a resource currently residing on the origin server should be modified to produce a new version.

In MSDN for the Delta class it's also clear, as the Patch description says:

Overwrites the original entity with the changes tracked by this Delta.

Unlike the description of Put:

Overwrites the original entity with the values stored in this Delta.

So far so good, but I couldn't find a way to send those "instructions" with OData, No matter what I do, Delta.Patch only replaces the values.

What should be the syntax of Patch request?

The ways I tried were:

PATCH http://localhost:55783/Products(1) HTTP/1.1
User-Agent: Fiddler
Host: localhost:55783
Content-Length: 19
Content-type: application/json

{ "Price": 432 }

And

{ "op": "add", "path": "/Price", "value": 423432 }

And stuff near that.


Update:

Thanks to Michael Moore and from reading the whole Delta class with ILSpy I think it's indeed a bug in the Patch verb design.
I opened a bug for Microsoft, you can vote on it if you need it to be fixed too.

Community
  • 1
  • 1
gdoron
  • 147,333
  • 58
  • 291
  • 367

1 Answers1

14

I'm not sure what you're trying to achieve is possible. At least not with Delta<TEntity>.Patch(..)

Assuming that you have Product entity and somewhere in your PATCH action you have

[AcceptVerbs("PATCH")]
public void Patch(int productId, Delta<Product> product)
{
    var productFromDb = // get product from db by productId
    product.Patch(productFromDb);
    // some other stuff
}

When product is created, internally it calls Delta<TEntityType> constructor, which looks like this (parameterless constructor also makes call to this one, passing typeof(TEntityType)

public Delta(Type entityType)
{
    this.Initialize(entityType);
}

Initialize method looks like this

private void Initialize(Type entityType)
{
    // some argument validation, emitted for the sake of brevity 

    this._entity = (Activator.CreateInstance(entityType) as TEntityType);
    this._changedProperties = new HashSet<string>();
    this._entityType = entityType;
    this._propertiesThatExist = this.InitializePropertiesThatExist();
}

Interesting part here is this._propertiesThatExist which is a Dictionary<string, PropertyAccessor<TEntityType>> which holds properties of the Product type. PropertyAccessor<TEntityType> is internal type to allow easier manipulation of properties.

When you call product.Patch(productFromDb) this is what is happening under the hood

// some argument checks
PropertyAccessor<TEntityType>[] array = (
        from s in this.GetChangedPropertyNames()
        select this._propertiesThatExist[s]).ToArray<PropertyAccessor<TEntityType>>();

    PropertyAccessor<TEntityType>[] array2 = array;

    for (int i = 0; i < array2.Length; i++)
    {
        PropertyAccessor<TEntityType> propertyAccessor = array2[i];
        propertyAccessor.Copy(this._entity, original);
    }

As you can see it gets properties that were changed, iterates over them and sets values from instance that was passed to Patch action to the instance you get from db. So the operation you're passing, property name and value to add are not going to reflect anything.

propertyAccessor.Copy(this._entity, original) method's body

public void Copy(TEntityType from, TEntityType to)
{
    if (from == null)
    {
        throw Error.ArgumentNull("from");
    }
    if (to == null)
    {
        throw Error.ArgumentNull("to");
    }
    this.SetValue(to, this.GetValue(from));
}
Michael
  • 2,961
  • 2
  • 28
  • 54
  • Thanks for your answer! I had the feeling using reflector could be helpful here... So is this a bug? Because the spec and MSDN both talk about "instructions" not values. So what's the difference between Delta.Put and Delta.Patch? By the way, do you work at Microsoft or "only" an enthusiastic programmer? – gdoron Sep 01 '14 at 23:39
  • @gdoron are you talking about MSDN for Delta's Patch method? – Michael Sep 01 '14 at 23:44
  • @gdoron you would probably be surprised, but Delta's PUT method doesn't differ that much, the only difference is that in PUT method it also goes thru the unchanged properties list and just copies those values also. And no, I don't work in MS :) Btw, just as a side note - I've used IlSpy not reflector :) – Michael Sep 01 '14 at 23:49
  • @gdoron as you mentioned in post - it just says it overwrites original entity with the changes _tracked_ by Delta. You saw how Delta tracks changes, it doesn't look for instructions and nowhere in MSDN I could find any word about _instructions_. I agree with you thou, _RFC spec_ talks about instructions. – Michael Sep 01 '14 at 23:52
  • So are you saying that Delta.Put doesn't partial update the entity but override the entity completely just like simple Http put method?! – gdoron Sep 01 '14 at 23:53
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/60421/discussion-between-michael-moore-and-gdoron). – Michael Sep 01 '14 at 23:54
  • [I opened a bug for Microsoft](https://connect.microsoft.com/VisualStudio/feedbackdetail/view/961798) Thanks! – gdoron Sep 02 '14 at 08:05
  • You can use `[HttpPatch]` instead of `[AcceptVerbs("PATCH")]`. –  May 24 '16 at 10:19