How to PATCH in Web API and OData
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));
}
Comments
-
gdoron is supporting Monica almost 2 years
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. -
gdoron is supporting Monica almost 10 yearsThanks 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?
-
Michael almost 10 years@gdoron are you talking about MSDN for Delta's Patch method?
-
Michael almost 10 years@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 almost 10 years@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.
-
gdoron is supporting Monica almost 10 yearsSo are you saying that Delta.Put doesn't partial update the entity but override the entity completely just like simple Http put method?!
-
Michael almost 10 yearsLet us continue this discussion in chat.
-
gdoron is supporting Monica almost 10 yearsI opened a bug for Microsoft Thanks!
-
Admin about 8 yearsYou can use
[HttpPatch]
instead of[AcceptVerbs("PATCH")]
.