2015-02-09 32 views
3

我來自Ruby on Rails API背景,但我目前正在研究.NET C#WebAPI。我熟悉C#和.NET webforms。.NET API更新包含ID

我試圖設置一個PUT請求,將更新數據庫中的記錄。腳手架的方法覆蓋所有的字段,而我只想簡單地更新通過PUT傳遞的字段。我嘗試用下面的代碼:

// PUT: api/Users/5 
     [ResponseType(typeof(void))] 
     public IHttpActionResult PutUser(string id, User user) 
     { 

      if (!ModelState.IsValid) 
      { 
       return BadRequest(ModelState); 
      } 

      User userToUpdate = db.Users.Where(u => u.id == id).FirstOrDefault(); 
      if (userToUpdate == null) 
      { 
       return NotFound(); 
      } 

      db.Entry(userToUpdate).CurrentValues.SetValues(user); 

      try 
      { 
       db.SaveChanges(); 
      } 
      catch (DbUpdateException) 
      { 
       if (UserExists(id)) 
       { 
        return Conflict(); 
       } 
       else 
       { 
        throw; 
       } 
      } 

      return StatusCode(HttpStatusCode.NoContent); 
     } 

然而,該方法對db.SaveChanges()失敗,因爲它不能覆蓋「身份證」,這是一個關鍵。 PUT請求是content-type: application/json和JSON下面是:

{ 
    "preferredEmailUpdates":"true" 
} 

這需要所有領域的工作,並接受空條目。所以下面也是有效的,並且應該用空值更新現場電話。

{ 
    "preferredEmailUpdates":"true", 
    "phone":null 
} 

如何在不更新密鑰的同時更新這些值?

+1

如果您不想設置所有值,則不能使用CurrentValues.SetValues(),則必須單獨複製每個字段。 – 2015-02-09 21:27:23

回答

2

您可以考慮使用PATCH HTTP動詞和Delta<T>對象。

[AcceptVerbs("Patch"), ResponseType(typeof(void))] 
    public IHttpActionResult PatchUser(string id, Delta<Team> changes) 
    { 
     User userToUpdate = db.Users.Where(u => u.id == id).FirstOrDefault(); 
     if (userToUpdate == null) 
      return NotFound(); 

     changes.Patch(userToUpdate); 

     try 
     {     
      db.SaveChanges() 
     } 
     catch (DbUpdateException) 
     { 
      ... 
     } 

     return StatusCode(HttpStatusCode.NoContent); 
    } 

Easy ASP.NET Web API resource updates with Delta見。

+1

作爲一個小心點,Delta只適用於OData格式的數據,而不是.NET Json。儘管它可以處理某些數據類型,但Int32等類型不能按預期運行。但是,如果您使用此處使用的修補程序(無雙關語),則可以採取以下措施:http://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/DeltaJsonDeserialization/DeltaJsonDeserialization.sln – steventnorris 2015-02-10 15:56:19

+0

爲什麼重新打開重複並且在這裏回答,在多個問題上傳播知識,而不是將答案添加到重複http://stackoverflow.com/questions/16137766/partial-entity-updates-in-webapi-put-post? – CodeCaster 2015-02-10 17:45:08

0

您的更新模型(用戶請求所綁定的類)應與您的數據庫模型分開(因爲它代表不同的概念)。它會有空或者可能的字段來表示可能的變化。它也會隱藏您不希望用戶更改的字段(如時間戳)。然後,您可以使用反射來僅更新修改的字段。考慮使用像AutoMapper這樣的隨時可用的庫

using System; 
using System.Reflection; 
using WhateverNamespaceIsNeededForWebApiAndEF; 

public class UserUpdate 
{ 
    public String Name {get; set;} 
    public bool? PreferredEmailUpdates {get; set;} 
} 
public class User 
{ 
    public int Id {get; set;} 
    public String Name {get; set;} 
    public bool PreferredEmailUpdates {get; set;} 
    public DateTimeOffset CreationDate {get; set;} 
} 

[ResponseType(typeof(void))] 
public IHttpActionResult PutUser(string id, UserUpdate command) 
{ 

    if (!ModelState.IsValid) 
    { 
     return BadRequest(ModelState); 
    } 

    User userToUpdate = db.Users.Where(u => u.id == id).FirstOrDefault(); 
    if (userToUpdate == null) 
    { 
     return NotFound(); 
    } 

    // THIS IS THE IMPORTANT CODE 
    PropertyInfo[] propInfos = command.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance); 
    foreach(var p in propInfos) 
    { 
     object value = p.GetValue(command); 
     if(value != null) 
     { 
      userToUpdate.GetType().GetProperty(p.Name).SetValue(userToUpdate, value); 
     } 
    } 
    // END IMPORTANT CODE 

    try 
    { 
     db.SaveChanges(); 
    } 
    catch (DbUpdateException) 
    { 
     if (UserExists(id)) 
     { 
      return Conflict(); 
     } 
     else 
     { 
      throw; 
     } 
    } 

    return StatusCode(HttpStatusCode.NoContent); 
} 

另一種情況是 - 爲什麼要更新單個字段?有能力更新電子郵件首選項而無需更新密碼是正常的,但是單獨更新所述首選項的每個字段是否正常?我對此表示懷疑。考慮把你的API分割成塊,就像你在OOP中分割大的接口一樣。

+0

這看起來像個不錯的開始,但是我又想要取消像{「email」:「testy.com」,「phone」:null}這樣的值的情況不會正確更新,因爲電話將被跳過。 – steventnorris 2015-02-09 21:35:14

+0

你應該考慮使用Maybe來表示修改的可能性。 AFAIK在.NET中沒有適當的類,但是構建起來很微不足道。 [這篇文章](http://twistedoakstudios.com/blog/Post1130_when-null-is-not-enough-an-option-type-for-c)看起來像是在解釋這個想法。 – joozek 2015-02-09 21:38:14