Как сделать патч в веб-API и OData
Из чтения спецификации RFC глагола Patch становится ясно, что Patch
глагол не должен получать значения для частичного обновления сущности, но должен выполнять следующие операции:
... Однако с помощью PATCH вложенный объект содержит набор инструкций, описывающих, как ресурс, находящийся в данный момент на исходном сервере, должен быть изменен для создания новой версии.
В MSDN для класса Delta это также ясно, как говорится в описании патча:
Перезаписывает исходную сущность изменениями, отслеживаемыми этой дельтой.
В отличие от описания Пут:
Перезаписывает исходную сущность значениями, хранящимися в этой дельте.
Пока все хорошо, но я не смог найти способ отправить эти "инструкции" с OData. Независимо от того, что я делаю, Delta.Patch только заменяет значения.
Каким должен быть синтаксис запроса Patch?
Пути, которые я попробовал, были:
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>>
который содержит свойства типа Product. 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, к экземпляру, который вы получаете из db. Таким образом, передаваемая вами операция, имя свойства и добавляемое значение не будут отражать ничего.
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));
}