2013-10-22 77 views
5

我正在使用HttpPatch部分更新對象。爲了得到這個工作,我使用了OData的Delta和Patch方法(在這裏提到:What's the currently recommended way of performing partial updates with Web API?)。似乎一切正常,但注意到映射器是區分大小寫的;當以下對象傳遞的屬性得到更新值:asp.net mvc web api部分更新OData修補程序

{ 
    "Title" : "New title goes here", 
    "ShortDescription" : "New text goes here" 
} 

但是當我通過與較低或駝峯性質相同的對象,補丁不起作用 - 新價值是不會通過,所以看起來像反序列化和屬性映射有問題,即:「shortDescription」到「ShortDescription」。

是否有一個配置節會忽略大小寫使用補丁?

FYI:

在輸出時我有使用以下格式駝峯特性(以下REST最佳實踐):

//formatting 
JsonSerializerSettings jss = new JsonSerializerSettings(); 
jss.ContractResolver = new CamelCasePropertyNamesContractResolver(); 
config.Formatters.JsonFormatter.SerializerSettings = jss; 

//sample output 
{ 
    "title" : "First", 
    "shortDescription" : "First post!" 
} 

我的模型類不過是follwing C#/。NET格式約定:

public class Entry { 
    public string Title { get; set;} 
    public string ShortDescription { get; set;} 
    //rest of the code omitted 
} 

回答

7

簡短的回答,沒有,沒有配置選項撤消的情況下敏感性(據我所知)

長答案:我今天遇到了同樣的問題,這就是我如何解決它的。
我發現它令人難以置信的煩人,它必須是大小寫敏感的,因此,我決定與整個OData的部分做掉,因爲它是我們在濫用一個巨大的圖書館....

這樣的例子實現可以在我的github找到github

我決定實現我自己的補丁方法,因爲那是我們實際上缺乏的肌肉。我創建了以下抽象類:

public abstract class MyModel 
{ 
    public void Patch(Object u) 
    { 
     var props = from p in this.GetType().GetProperties() 
        let attr = p.GetCustomAttribute(typeof(NotPatchableAttribute)) 
        where attr == null 
        select p; 
     foreach (var prop in props) 
     { 
      var val = prop.GetValue(this, null); 
      if (val != null) 
       prop.SetValue(u, val); 
     } 
    } 
} 

然後我把我所有的模型類從*爲MyModel *繼承。注意我使用* let *的行,稍後我會說。所以現在你可以從你的控制器動作中刪除Delta,並且再次使它成爲Entry,就像put方法一樣。例如

public IHttpActionResult PatchUser(int id, Entry newEntry) 

,您仍然可以使用修補方法,你習慣的方式:

var entry = dbContext.Entries.SingleOrDefault(p => p.ID == id); 
newEntry.Patch(entry); 
dbContext.SaveChanges(); 

現在,讓我們回到線

let attr = p.GetCustomAttribute(typeof(NotPatchableAttribute)) 

我發現它的安全風險只要任何屬性都可以使用修補程序請求進行更新。例如,您現在可能希望修補程序可以更改ID。我創建了一個自定義屬性來裝飾我的屬性。在NotPatchable屬性:

public class NotPatchableAttribute : Attribute {} 

你可以使用它就像任何其他的屬性:

public class User : MyModel 
{ 
    [NotPatchable] 
    public int ID { get; set; } 
    [NotPatchable] 
    public bool Deleted { get; set; } 
    public string FirstName { get; set; } 
} 

這本叫刪除,ID屬性不能雖然打補丁方法來改變。

我希望這能爲你解決它。如果您有任何問題,請不要猶豫留下評論。

我在一個新的mvc 5項目中添加了一個檢查道具的截圖。正如你所看到的,結果視圖中填充了Title和ShortDescription。

Example of inspecting the props

+0

嗨裏克,謝謝你的回答, 我加入的代碼,但也有一些差異(使用MVC 5現在),它不工作: ' VAR道具= p in this.GetType()。GetProperties() let attr = p.GetCustomAttributes(typeof(NotPatchableAttribute),false) where attr == null select p; ' 但是,該查詢不會返回任何內容。 – 303

+0

@ 303 Oh ..我正在使用web api 2,讓我快速在mvc 5上測試它,然後我會回覆你..沒有東西是不可溶解的:) –

+0

@ 303我剛剛在一個新的mvc項目中測試它,似乎工作。爲了速度的緣故,我只是修改了編輯方法來使用補丁,並且它工作正常。我會把代碼放在github上供你比較。 –

2

可以與繼承CamelCasePropertyNamesContractResolver和實施CreateContract方法看具體型號爲三角洲和使用來自JSON的一個得到實際的屬性名稱,而不是一個自定義的合同解析器很容易做。摘要如下:

public class DeltaContractResolver : CamelCasePropertyNamesContractResolver 
{ 
     protected override JsonContract CreateContract(Type objectType) 
     { 
      // This class special cases the JsonContract for just the Delta<T> class. All other types should function 
      // as usual. 
      if (objectType.IsGenericType && 
       objectType.GetGenericTypeDefinition() == typeof(Delta<>) && 
       objectType.GetGenericArguments().Length == 1) 
      { 
       var contract = CreateDynamicContract(objectType); 
       contract.Properties.Clear(); 

       var underlyingContract = CreateObjectContract(objectType.GetGenericArguments()[0]); 
       var underlyingProperties = 
        underlyingContract.CreatedType.GetProperties(BindingFlags.Public | BindingFlags.Instance); 
       foreach (var property in underlyingContract.Properties) 
       { 
        property.DeclaringType = objectType; 
        property.ValueProvider = new DynamicObjectValueProvider() 
        { 
         PropertyName = this.ResolveName(underlyingProperties, property.PropertyName), 
        }; 

        contract.Properties.Add(property); 
       } 

       return contract; 
      } 

      return base.CreateContract(objectType); 
     } 

     private string ResolveName(PropertyInfo[] properties, string propertyName) 
     { 

      var prop = properties.SingleOrDefault(p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)); 

      if (prop != null) 
      { 
       return prop.Name; 
      } 

      return propertyName; 
     } 
} 
+2

這工作對我來說,但我缺少'DynamicObjectValueProvider',我發現實現[這裏](https://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/DeltaJsonDeserialization/DeltaJsonDeserialization.Server/DeltaContractResolver.cs)除了DeltaContractResolver實現,我無法工作 –

+1

如果其他人想知道,你可以在WebApiConfig中設置合約解析器,像這樣 'config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new DeltaContractResolver();' – Maetis

相關問題