Like PATCH in web API and OData

From reading the RFC specification of the Patch verb, it is clear that the Patch verb should not receive values โ€‹โ€‹for partial updating of the object, but the operation:

... Using PATCH, the nested object contains a set of instructions that describe how the resource currently located on the source server should be changed to create a new version.

On MSDN for the Delta class, this is also clear, as the Patch description says:

Overwrites the original object with the tracked changes of this Delta.

Unlike the Put description:

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

So far so good, but I could not find a way to send these โ€œinstructionsโ€ using OData. No matter what I do, Delta.Patch only replaces the values.

What should be the syntax of the Patch request?

I have tried:

 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 everything is nearby.


Update:

Thanks to Michael Moore and reading the entire Delta class with ILSpy, I believe this is really a design flaw in the Patch verb.
I opened a bug for Microsoft, you can vote for it if you need to fix it.

+6
source share
1 answer

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

Assuming you have a 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 a Product is created, inside it calls the Delta<TEntityType> constructor, which looks like this (a parameterless constructor also calls this, passing typeof(TEntityType)

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

Initialize method is as follows

 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(); } 

The interesting part here is this._propertiesThatExist , which is a Dictionary<string, PropertyAccessor<TEntityType>> , which contains properties of the product type. PropertyAccessor<TEntityType> is an internal type that simplifies property management.

When you call product.Patch(productFromDb) , this is what happens 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 the changed properties, iterates over them and sets the values โ€‹โ€‹from the instance passed to the Patch action to the instance you get from db. So the operation you pass in, the property name and the value to add do not reflect anything.

propertyAccessor.Copy(this._entity, original) method 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)); } 
+9
source

Source: https://habr.com/ru/post/974650/


All Articles