2015-10-14 55 views
6

目前我在做這樣的:最佳途徑只更新修改的字段與實體框架

例如:

public update(Person model) 
{ 
    // Here model is model return from form on post 
    var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault(); 
    db.Entry(oldobj).CurrentValues.SetValues(model); 
} 

它的工作原理,但例如,

我有50列我的表格,但我只顯示了我的表格中的25個字段(我需要部分更新我的表,其餘25列保留相同的舊值)

我知道它可以通過「繪圖柱一個接一個「或通過爲剩餘的25列創建」隱藏字段「。

只是想知道是否有任何優雅的方式,以較少的努力和最佳性能做到這一點?

+0

創建視圖模型..只添加你想要的領域..設定值 – JamieD77

+0

即使我創建視圖模型;更新到我的表「Person」時仍存在同樣的問題。如果我缺少一些東西,請糾正我的問題 –

+0

您的viewmodel是否只顯示了您顯示的25個字段? – JamieD77

回答

5

這是一個非常好的問題。默認情況下,我發現只要啓用了更改跟蹤(這是默認情況下除非關閉),Entity Framework將會很好地向數據庫應用您要求更改的內容。

因此,如果您只改變對象的1個字段,然後調用SaveChanges(),EF只會在調用SaveChanges()時更新該字段。

這裏的問題是,當您將視圖模型映射到實體對象時,所有的值都會被覆蓋。這裏是我的處理是這樣的:

在這個例子中,有一個名爲的人一個單一的實體:

Person 
====== 
Id - int 
FirstName - varchar 
Surname - varchar 
Dob - smalldatetime 

現在讓我們說,我們要創建一個將只更新杜伯視圖模型,並留下所有其他領域究竟如何,這是我如何做到這一點。

首先,創建一個視圖模型:

public class PersonDobVm 
{ 
    public int Id { get; set; } 
    public DateTime Dob { get; set; } 

    public void MapToModel(Person p) 
    { 
     p.Dob = Dob; 
    } 
} 

現在寫的代碼大致如下(你必須改變它來匹配您的上下文的名稱等):

DataContext db = new DataContext(); 
Person p = db.People.FirstOrDefault(); 

// you would have this posted in, but we are creating it here just for illustration 
var vm = new PersonDobVm 
{ 
    Id = p.Id, // the Id you want to update 
    Dob = new DateTime(2015, 1, 1) // the new DOB for that row 
}; 

vm.MapToModel(p); 
db.SaveChanges(); 

的MapToModel方法可能會更復雜,並在將視圖模型字段分配給實體對象之前執行各種附加檢查。

無論如何,當調用SaveChanges結果是下面的SQL:

exec sp_executesql N'UPDATE [dbo].[Person] 
SET [Dob] = @0 
WHERE ([Id] = @1) 
',N'@0 datetime2(7),@1 int',@0='2015-01-01 00:00:00',@1=1 

所以,你可以清楚地看到,實體框架並沒有試圖更新任何其他領域 - 只是杜伯領域。

我知道你的例子中你想避免用手來編碼每個任務,但我認爲這是最好的方法。你把它全部放在你的虛擬機中,這樣它就不會拋棄你的主代碼,這樣你就可以滿足特定的需求(即那裏的複合類型,數據驗證等)。另一種選擇是使用AutoMapper,但我不認爲它們是安全的。如果您在虛擬機中使用AutoMapper並將「Dob」拼寫爲「Doob」,則它不會將「Doob」映射到「Dob」,也不會告訴您!它會默默地失敗,用戶會認爲一切正常,但變化不會被保存。如果你在虛擬機中拼寫「Dob」爲「Doob」,編譯器會提醒你MapToModel()引用了「Dob」,但你的虛擬機中只有一個名爲「Doob」的屬性。

我希望這可以幫助你。

+0

感謝您的建議,但一對一的映射是當你有大量coulmns相當忙碌。 –

0

我已使用的FormCollection列出了在形式使用元素,只有改變數據庫的那些列解決我的問題。

我提供我下面的代碼示例;偉大的,如果它可以幫助別人

// Here 
// collection = FormCollection from Post 
// model = View Model for Person 

var result = db.Person.Where(x => x.ID == model.ID).SingleOrDefault(); 
if (result != null) 
{ 
    List<string> formcollist = new List<string>(); 
    foreach (var key in collection.ToArray<string>()) 
    { 
     // Here apply your filter code to remove system properties if any 
     formcollist.Add(key); 
    } 
    foreach (var prop in result.GetType().GetProperties()) 
    { 
      if(formcollist.Contains(prop.Name)) 
      { 
        prop.SetValue(result, model.GetType().GetProperty(prop.Name).GetValue(model, null)); 
      } 
    } 
    db.SaveChanges(); 
} 
+2

雖然這可能會起作用併爲您節省了一些編碼,但您需要考慮的問題是:當有人在您的視圖模型中更改屬性名稱時,此代碼的工作性能如何?想象一下,在重構期間有人將「Dob」更改爲「DateOfBirth」。你的代碼將如何工作呢?你根本沒有編譯器的安全性。將會發生什麼是用戶將在「DateOfBirth」中輸入一個值,但它不會被應用到Dob字段。用戶說他們進入了Dob,但是你的數據庫會說他們沒有。 –

4

我發誓通過EntityFramework.Extended。 Nuget Link

它可以讓你寫:

db.Person 
    .Where(x => x.ID == model.ID) 
    .Update(p => new Person() 
    { 
    Name = newName, 
    EditCount = p.EditCount+1 
    }); 

,這是非常清楚的轉換成SQL。

+0

我認爲.Update在實體框架 –

+4

EntityFramework.Extended中不可用。這是一個nuget包。 – Visser

+0

這是漂亮的,但我發現它在使用Web API 2結合使用EF時,並沒有真正的幫助,因爲'.Update()'方法來實例化類型(否則你會得到一個'MemberInitException')。如果我要做到這一點,我需要通過我的入站對象的屬性循環以實例所述方法:( – LeeCambl