接續上一篇 ADO.Net Entity Framework : (十五) 深入探討,物件間傳遞,以及EntityKey、EntityState,
探討了如何將 Entity Object 在 ObjectContext 間傳遞,
這篇延續上篇介紹的技巧,來討論如何對已中斷物件執行變更,
適用在使用ObjectDataSource、分散式系統、 WebService等情境
接續上一篇 ADO.Net Entity Framework : (十五) 深入探討,物件間傳遞,以及EntityKey、EntityState
探討了如何將 Entity Object 在 ObjectContext 間傳遞,
這篇延續上篇介紹的技巧,來討論如何對已中斷物件執行變更,
適用在使用ObjectDataSource、分散式系統、 WebService等情境
一樣先來看一下範例資料庫的 ER-Modal ,讓大家對範例的資料結構清楚,接下來看範例比較了解語法
這邊說明一下需求,
我想建立一個 Class ,命名為 【DataAccessClass】,提供兩個Mothed,
- 【GetData】:透過 ADO.Net Entity Framework 去 資料庫中取得指定的資料
- 【Update】 :接受參數,並透過 ADO.Net Entity Framework 將資料更新至資料庫中
在主程式中執行以下步驟
- 透過 GetItem Mothed 取得資料
- 修改這個資料的內容
- 利用 Update Mothed 將資料儲存回資料庫。
因此我用上次介紹的技巧,寫了一個範例
範例一
DataAccessClass
public class DataAccessClass
{
//// 取得資料
public static User GetData(int userid)
{
using (TestEntities te = new TestEntities())
{
var u = te.User.Execute(MergeOption.NoTracking).Where(a => a.User_id == userid).FirstOrDefault();
////利用EntityObject.Execute(MergeOption.NoTracking),效果相同於使用ObjectContext.Dettach(EntityObject)
////如果此時觀察 u.EntityState,其值會為 Dettached
return u;
}
}
//// 利用Attech方法,將異動寫入資料庫
public static void Update(User item)
{
using (TestEntities te = new TestEntities())
{
te.Attach(item);
te.SaveChanges();
}
}
}
主程式
////取得資料
var u = DataAccessClass.GetData(2);
Response.Write("修改前:" + u.User_email);
////修改內容
u.User_email = "test@mail.com";
////將資料儲存
DataAccessClass.Update(u);
////重新取得資料並顯示
u = DataAccessClass.GetData(2);
Response.Write("修改後:" + u.User_email);
說明
一開始利用GetData Mothed 取得資料後,
將 User 的 Email 修改,再將修改過的 User 資料利用 Update Mothed, 更新至資料庫,
其中有用到 ObjectQuery類別的 Execute(MergeOption.NoTracking) 方法,其效果與【Dettech】相同。
整個程式碼看起來正常,也預期執行完資料會被異動,
但是執行後結果卻發現資料沒有異動@@,
執行結果如下
這邊說明一下為什麼資料沒有被更新,
當利用【Attech】方法去附加 EntityObject 至 ObjectContext 時,
EntityObject 的 EntityState 會是 Unchanged,這表示該資料不會被寫回資料庫,
所以執行完才會發現資料沒有異動,
那我們又需要對 中斷物件(EntityState = Dettech 的 EntityObject) 進行變更的話該怎麼改寫?
下面提供兩種做法
範例二 利用ApplyPropertyChanges方法,將異動寫入資料庫
DataAccessClass
public class DataAccessClass
{
//// 取得資料
public static User GetData(int userid)
{
using (TestEntities te = new TestEntities())
{
var u = te.User.Execute(MergeOption.NoTracking).Where(a => a.User_id == userid).FirstOrDefault();
return u;
}
}
// 利用ApplyPropertyChanges方法,將異動寫入資料庫
public static void Update(User item)
{
using (TestEntities te = new TestEntities())
{
te.GetObjectByKey(item.EntityKey);
te.ApplyPropertyChanges(item.EntityKey.EntitySetName, item);
te.SaveChanges();
}
}
}
主程式
////取得資料
var u = DataAccessClass.GetData(2);
Response.Write("修改前:" + u.User_email + "<br/>");
////修改內容
u.User_email = "test@mail.com";
////將資料儲存
DataAccessClass.Update(u);
////重新取得資料並顯示
u = DataAccessClass.GetData(2);
Response.Write("修改後:" + u.User_email);
執行結果
這次執行就有順利將異動寫入資料庫囉
說明
針對幾個用到的 Mothed 進行說明
【GetObjectByKey】、【TryGetObjectByKey】
可透過此 Mothed ,利用EntityKey取得 EntityObject。
還記得上一篇有介紹過,一個EntityKey可以定義一筆資料,
因此利用 GetObjectByKey 傳入 EntityKey,
ObjectContext 就可以取得那筆資料,
【GetObjectByKey】 vs 一般查詢
那透過GetObjectByKey 跟 直接下語法去查詢差別在哪?
運作的方式不同,使用 GetObjectByKey 時,你必須先建立 EntityKey,
然後執行 GetObjectByKey 時,會先檢查 ObjectContext 內的快取,是不是有相同的資料,
有的話就,直接傳回,不會進資料庫撈資料,提升效能,
沒有的話,在進資料庫撈資料
【ApplyPropertyChanges】
此Mothed可以將 中斷物件 的值,覆蓋到 ObjectContext 上相同主鍵(EntityKey) 的 EntityObject,
且被覆蓋的 EntityObject 狀態會改為 EntityState.Modified,
因此我們可以利用此方法進行資料變更
【ApplyPropertyChanges】 vs 【Attech】
Attech 可以將中斷連結物件附加至ObjectContext,附加後狀態為 EntityState.Unchanged,
ApplyPropertyChanges 可以將中斷連結物件的值,覆蓋到已附加的物件上,並修改已附加物件的狀態為 EntityState.Modified
在說明完以上幾個重要的Mothed後,再回頭來看 DataAccess.Update 流程
- 利用參數將異動後的 User 傳入
- 執行 te.GetObjectByKey(item.EntityKey) 取得此筆資料
- 執行 te.ApplyPropertyChanges(item.EntityKey.EntitySetName, item) ,
將 傳入的 User 物件 的值,覆蓋,並記錄為 EntityState.modified - 執行 te.SaveChanges() ,將資料寫入資料庫
- 完工!!
範例二可以解決需求並正確將資料異動,但中間的過程有疑問,
我已經將資料用參數傳入了,
但是在更新之前,會再去跟資料庫要一次資料,做了兩次工,浪費效能,
那如果我想直接以傳入的物件,利用Attech,將異動直接寫入資料庫的話,該怎麼做?
上面有提到 Attech 後的物件 EntityState 會是 Unchanged,
因此我只要把 EntityState 修改成 Modified,是不是就會寫入資料庫?
以此方向著手,下面再提供一個範例
範例三 利用 Attech 方法搭配 SetModifiedProperty,將異動寫入資料庫
DataAccessClass
public class DataAccessClass
{
//// 取得資料
public static User GetData(int userid)
{
using (TestEntities te = new TestEntities())
{
var u = te.User.Execute(MergeOption.NoTracking).Where(a => a.User_id == userid).FirstOrDefault();
return u;
}
}
// 利用Attech方法,將異動寫入資料庫
public static void Update(User item)
{
using (TestEntities te = new TestEntities())
{
te.Attach(item);
te.SetAllModified(item);
te.SaveChanges();
}
}
}
ObjectContextExtension
public static class ObjectContextExtension
{
public static void SetAllModified(this ObjectContext objectContext, IEntityWithKey item)
{
ObjectStateEntry stateEntry = objectContext.ObjectStateManager.GetObjectStateEntry(item) as ObjectStateEntry;
IEnumerable propertyNameList = stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(pn => pn.FieldType.Name);
foreach (string propName in propertyNameList)
{
stateEntry.SetModifiedProperty(propName);
}
stateEntry.SetModified();
}
}
主程式
////取得資料
var u = DataAccessClass.GetData(2);
Response.Write("修改前:" + u.User_email + "<br/>");
////修改內容
u.User_email = "test2@mail.com";
////將資料儲存
DataAccessClass.Update(u);
////重新取得資料並顯示
u = DataAccessClass.GetData(2);
Response.Write("修改後:" + u.User_email);
執行結果
說明
一樣,先來介紹幾個重要的方法
【ObjectStateEntry.SetModifiedProperty】
將指定的屬性標記為已修改。
【ObjectStateEntry.SetModified】
將物件或關聯性的狀態設定為 Modified。
這邊說明一下,Attech 方法附加的 EntityObject 其 EntityState = Unchanged,
為了要可以將異動寫入資料庫,
利用了 ObjectStateEntry.SetModifiedProperty 方法,來將屬性標記為已修改,
以及 ObjectStateEntry.SetModified 將 EntityObject 的 EntityState 改為 Modified,
但是呢,沒有提供可以把所有屬性都標示為已修改的方法,
因此我寫了一個 ObjectContextExtension.SetAllModified,
將所有屬性都標示為已修改。
再回頭來看 DataAccess.Update 流程
- 利用參數將異動後的 User 傳入
- 執行 te.Attach(item); 附加至ObjectContext
- 執行 te.SetAllModified(item) ,將物件、物件屬性標示為Modified
- 執行 te.SaveChanges() ,將資料寫入資料庫
- 完工!!
兩種方法比較 【Attech 搭配 SetModifiedProperty】vs【ApplyPropertyChanges 】
Attech 搭配 SetModifiedProperty | ApplyPropertyChanges |
異動時,不需要去資料庫取回資料,可直接用Attech的物件來進行異動 | 必須在ObjectContext有此資料才可呼叫此方法,因此實作上會先使用 GetObjectByKey 取得資料 |
可指定要異動的屬性 | 全部屬性都會異動 |
我自己在使用ADO.Net Entity Framework時,因為架構關係,
常使用ObjectDataSource,就會常常碰到這兩篇文章討論的主題,
上面是我到處找資料研究出來的心得,
希望對大家有幫助 ^_^
參考連結
ObjectContext..::.ApplyPropertyChanges 方法
ObjectContext..::.GetObjectByKey 方法
ObjectStateEntry 類別
ObjectStateManager..::.TryGetObjectStateEntry 方法 (EntityKey, ObjectStateEntry%)
ObjectStateEntry..::.SetModifiedProperty 方法
ObjectStateEntry..::.SetModified 方法
HOW TO:使用特定物件的索引鍵傳回此物件 (Entity Framework)
HOW TO:套用對中斷連結的物件所做的變更 (Entity Framework)
ADO.NET Entity Framework(3)ObjectContext <--簡體版說明,資料非常詳細
- 如果您覺得這篇文章有幫助,請您幫忙推薦一下或按上方的"讚"給予支持,非常感激
- 歡迎轉載,但請註明出處
- 文章內容多是自己找資料學習到的心得,如有不詳盡或錯誤的地方,請多多指教,謝謝