Asp.net-Mvc

使用實體框架僅更新已修改欄位的最佳方法

  • January 29, 2022

目前我正在這樣做:

例如:

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 列的隱藏欄位”來實現。

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

這個問題問得好。預設情況下,我發現只要啟用更改跟踪(預設情況下,除非您將其關閉),Entity Framework 會很好地僅將您要求更改的內容應用於數據庫。

因此,如果您僅針對對象更改 1 個欄位,然後呼叫 SaveChanges(),則 EF 只會在您呼叫 SaveChanges() 時更新該 1 個欄位。

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

在此範例中,您有一個名為 Person 的實體:

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

現在假設我們要創建一個只會更新 Dob 的視圖模型,並讓所有其他欄位保持原樣,這就是我的做法。

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

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

所以你可以清楚地看到,Entity Framework 沒有嘗試更新任何其他欄位——只是 Dob 欄位。

我知道在您的範例中您希望避免手動編碼每個作業,但我認為這是最好的方法。你把它全部藏在你的虛擬機裡,這樣它就不會亂扔你的主程式碼,這樣你就可以滿足特定的需求(即那裡的複合類型、數據驗證等)。另一種選擇是使用 AutoMapper,但我認為它們不安全。如果您使用 AutoMapper 並在您的 VM 中將“Dob”拼寫為“Doob”,它不會將“Doob”映射到“Dob”,也不會告訴您它!它會默默地失敗,使用者會認為一切正常,但不會保存更改。

然而,如果您在 VM 中將“Dob”拼寫為“Doob”,編譯器會提醒您 MapToModel() 正在引用“Dob”,但您的 VM 中只有一個名為“Doob”的屬性。

我希望這可以幫助你。

引用自:https://stackoverflow.com/questions/33137514