Как исправить в Web API и OData
Из чтения спецификации RFC глагола Patch ясно, что глагол Patch
должен получать значения не для частичного обновления сущности, а для выполнения операций:
...Вместе с патчем, однако, вложенная сущность содержит набор инструкций, описывающих, как ресурс в настоящее время находится на исходный сервер должен быть изменен для создания новой версии.
В MSDN для класса Delta это также понятно, как и описание патча говорит:
Перезаписывает исходную сущность с помощью изменений, отслеживаемых этой Дельтой.
В отличие от описания ставим :
Пока все хорошо, но я не мог найти способ отправить эти "инструкции" с OData, независимо от того, что я делаю, Дельта.Патч только заменяет значения.Перезаписывает исходную сущность значениями , хранящимися в этой дельте.
Каким должен быть синтаксис запроса на исправление?
Способы, которые я пробовал, были:
PATCH http://localhost:55783/Products(1) HTTP/1.1
User-Agent: Fiddler
Host: localhost:55783
Content-Length: 19
Content-type: application/json
{ "Price": 432 }
И
{ "op": "add", "path": "/Price", "value": 423432 }
И все такое прочее.
Обновление:
Благодаря Майклу Муру и прочитав весь класс Delta с ILSpy, я думаю, что это действительно ошибка в дизайне глагола Patch.
я открыл ошибку для Microsoft, вы можете проголосовать за нее, если вам нужно, чтобы она тоже была исправлена.
1 ответ:
Я не уверен, что то, чего вы пытаетесь достичь, возможно. По крайней мере, не с
Delta<TEntity>.Patch(..)
Предполагая, что у вас есть
Product
сущность и где-то в вашемPATCH
действии у вас есть[AcceptVerbs("PATCH")] public void Patch(int productId, Delta<Product> product) { var productFromDb = // get product from db by productId product.Patch(productFromDb); // some other stuff }
Когда
product
создается, внутренне он вызываетDelta<TEntityType>
конструктор, который выглядит так (конструктор без параметров также вызывает этот, передаваяtypeof(TEntityType)
public Delta(Type entityType) { this.Initialize(entityType); }
Initialize
метод выглядит следующим образом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(); }
Интересной частью здесь является
this._propertiesThatExist
, которая являетсяDictionary<string, PropertyAccessor<TEntityType>>
, которая содержит свойства типа продукта.PropertyAccessor<TEntityType>
- это внутренний тип, позволяющий легче манипулировать свойствами.Когда вы звоните
product.Patch(productFromDb)
это то, что происходит под капотом// 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); }
Как вы можете видеть, он получает свойства, которые были изменены, перебирает их и устанавливает значения из экземпляра, который был передан в действие Patch, в экземпляр, который вы получаете из БД. Таким образом, передаваемая операция, имя свойства и добавляемое значение ничего не будут отражать.
propertyAccessor.Copy(this._entity, original)
тело метода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)); }