【问题标题】:How to PATCH in Web API and OData如何在 Web API 和 OData 中打补丁
【发布时间】:2014-10-25 19:47:12
【问题描述】:

从阅读RFC specification of the Patch verb 可以清楚地看出,Patch 动词不应该获取值来部分更新实体,而是要进行操作:

...但是,使用 PATCH,封闭的实体包含一个集合 描述资源当前如何驻留在 应修改源服务器以生成新版本。

在 MSDN 中的 Delta 类也很清楚,正如 Patch 描述所说:

使用此 Delta 跟踪的更改覆盖原始实体。

不同于Put的描述:

用存储在此 Delta 中的覆盖原始实体。

到目前为止一切顺利,但我找不到使用 OData 发送这些“指令”的方法,无论我做什么,Delta.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 }

还有附近的东西。


更新:

感谢 Michael Moore 并通过 ILSpy 阅读了整个 Delta 课程,我认为这确实是 Patch 动词设计中的一个错误。
I opened a bug for Microsoft, you can vote on it if you need it to be fixed too.

【问题讨论】:

    标签: c# .net asp.net-web-api odata http-patch


    【解决方案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&lt;TEntityType&gt;构造函数,看起来像这样(无参构造函数也调用这个,传递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&lt;string, PropertyAccessor&lt;TEntityType&gt;&gt;,它包含产品类型的属性。 PropertyAccessor&lt;TEntityType&gt; 是内部类型,可以更轻松地操作属性。

    当您致电 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));
    }
    

    【讨论】:

    • 感谢您的回答!我觉得在这里使用反射器可能会有所帮助......所以这是一个错误吗?因为规范和 MSDN 都在谈论“指令”而不是价值。那么 Delta.Put 和 Delta.Patch 有什么区别呢?顺便问一下,你是在微软工作还是“只是”一个热心的程序员?
    • @gdoron 你是在谈论 MSDN 中的 Delta 的 Patch 方法吗?
    • @gdoron 你可能会感到惊讶,但是 Delta 的 PUT 方法并没有太大的不同,唯一的区别是在 PUT 方法中它也通过未更改的属性列表并复制这些值。不,我不在 MS 工作 :) 顺便说一句,顺便说一句 - 我使用的是 IlSpy 而不是反射器 :)
    • @gdoron 正如您在帖子中提到的那样 - 它只是说它用 Delta 跟踪 的更改覆盖了原始实体。您看到了 Delta 如何跟踪更改,它不查找指令,而且在 MSDN 中我找不到任何关于 instructions 的词。我同意你的看法,RFC 规范 谈论说明。
    • 那么你是说Delta.Put不会部分更新实体,而是像简单的Http put方法一样完全覆盖实体?!
    猜你喜欢
    • 2014-06-11
    • 1970-01-01
    • 1970-01-01
    • 2012-05-07
    • 2015-03-30
    • 2020-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多