2015-10-15 89 views
5

我有運行實體框架WCF服務器應用程序6.實體框架6 - DataServiceContext檢測也已經被修改

我的客戶端應用程序消耗的OData從通過DataServiceContext的服務器,在我的客戶端代碼中,我希望能夠調用上下文中的HasChanges()方法以查看其中的任何數據是否已更改。

我試着用以下的擴展方法:

public static bool HasChanges(this DataServiceContext ctx) 
    { 
     // Return true if any Entities or links have changes 
     return ctx.Entities.Any(ed => ed.State != EntityStates.Unchanged) || ctx.Links.Any(ld => ld.State != EntityStates.Unchanged); 
    } 

但它始終返回false,即使是跟蹤實體確實有改變。

例如,假設我有一個名爲Customer的跟蹤實體,下面的代碼在調用SaveChanges()之前總是返回。

Customer.Address1 = "Fred" 
    if not ctx.HasChanges() then return 
    ctx.UpdateObject(Customer) 
    ctx.SaveChanges() 

如果我註釋掉如果不是ctx.HasChanges(),然後返回行代碼,改變被成功保存,所以我很高興的是,實體已收到的變化,並能保存。

看來,變化越來越上下文跟蹤,只是我不能確定從我的代碼這一事實。

誰能告訴我如何確定DataServiceContext上的HasChanges?

+0

也許我不明白用例,但爲什麼不直接調用SaveChanges()呢?如果沒有改變,EF不會做任何事情。據推測,EF在內部做了類似的事情,而你正在重新發明輪子。 – Vlad274

+0

感謝Vlad,我想在實際保存數據之前彈出一個對話框,說出「你確定要保存更改」。如果沒有變化,我不想彈出對話框。 –

回答

2

遠了。我剛剛閱讀了DataServiceContext.UpdateObjectInternal(entity, failIfNotUnchanged),這是從UpdateObject(entity)直接調用false參數。

邏輯讀象:

  • 如果已經修改,返回; (短路)
  • 如果不變,則拋出if failIfNotUnchanged; (僅從ChangeState()開始)
  • 其他設置狀態爲已修改。 (無數據檢查發生)

所以由它的外觀,UpdateObject不關心/檢查實體的內部狀態,只是State枚舉。這使得更新在沒有變化時感覺有點不準確。

然而,我覺得你的問題是那麼的代碼OP第二塊,你檢查你的分機呼叫UpdateObjectHasChanges之前。這些實體只是榮耀的POCO(因爲您可以在您的Reference.cs(顯示隱藏文件,然後在服務參考下)中閱讀)。他們有顯而易見的屬性和一些On-操作來通知有關更改。他們做的不是在內部做的是跟蹤狀態。實際上,有一個EntityDescriptor與該實體關聯,該實體負責在EntityTracker.TryGetEntityDescriptor(entity)中進行狀態跟蹤。

底線是操作實際的工作很簡單,我想你只需要讓你的代碼像

Customer.Address1 = "Fred"; 
ctx.UpdateObject(Customer); 
if (!ctx.HasChanges()) return; 
ctx.SaveChanges(); 

儘管我們現在知道,這將總是報告HasChanges == true,這樣你不妨跳過檢查。

但是不要絕望!您的服務參考提供的部分類可能會擴展到您想要的。這完全是樣板代碼,所以你可能想寫一個.tt或其他代碼。無論如何,只要調整這個你的實體:

namespace ODataClient.ServiceReference1 // Match the namespace of the Reference.cs partial class 
{ 
    public partial class Books // Your entity 
    { 
     public bool HasChanges { get; set; } = false; // Your new property! 

     partial void OnIdChanging(int value) // Boilerplate 
     { 
      if (Id.Equals(value)) return; 
      HasChanges = true; 
     } 

     partial void OnBookNameChanging(string value) // Boilerplate 
     { 
      if (BookName == null || BookName.Equals(value)) return; 
      HasChanges = true; 
     } 
     // etc, ad nauseam 
    } 
    // etc, ad nauseam 
} 

但是現在這個偉大工程,也同樣表現在OP:

var book = context.Books.Where(x => x.Id == 2).SingleOrDefault(); 
book.BookName = "W00t!"; 
Console.WriteLine(book.HasChanges); 

HTH!

+0

託德你很棒=)你把我放在了我需要看的地方。我最終用'HasChanges()'屬性創建了一個部分類。在構造函數中,我爲PropertyChanged'PropertyChanged + = Customer_PropertyChanged;'創建了一個委託,並在我的部分類中實現了一個事件處理函數,如下所示:'private void Customer_PropertyChanged(object sender,PropertyChangedEventArgs e)=> HasChanges = true;'。在我加載實體後,我將'HasChanges()'設置爲false。任何觸發PropertyChanged事件的屬性都會將實體標記爲發生更改。謝謝! –

+1

**附加:**剛剛發現上述解決方案的小問題。在部分類中創建屬性'HasChanges()'會導致實體相信它有一個名爲** HasChanges **的數據庫列。而不是使用屬性,我最終使用一個私人變量與一個單獨的getter和setter方法。乾杯。 –

2

難道你不能正確地添加/編輯你的實體嗎? MSDN指出您必須使用AddObjectUpdateObject,或DeleteObject得到改變跟蹤射擊在客戶端上(https://msdn.microsoft.com/en-us/library/gg602811(v=vs.110).aspx - 見管理併發)。否則你的擴展方法看起來不錯。

+0

謝謝託德。在我的上下文中的對象在客戶端訪問之前通過OData加載到上下文中。 (沿用ctx.Customers中的c,其中c.CustomerId == CustomerId選擇c).FirstOrDefault();'上下文合併選項保留默認值,用於啓用更改跟蹤。變更已成功發送回數據庫,似乎我自己無法看到這些更改。 –

+0

你需要Attach()嗎? –

+0

以前我沒有見過'Attach()'方法,感謝這個建議,但是不確定它會幫助我,因爲它看起來像是爲了讓斷開連接的實體進入上下文,所以它們可以被更新而不是被插入。我的實體肯定是在上下文中並被正確跟蹤和更新。 –

0

爲了使其工作,必須啓用自動更改跟蹤功能。您可以在

ctx.Configuration.AutoDetectChangesEnabled 

此設置的所有實體對象也必須由上下文ctx跟蹤。 這意味着它們必須由ctx的方法之一返回或顯式添加到上下文中。

這也意味着它們必須由DataServiceContext的同一實例進行跟蹤。你以某種方式創造多個上下文嗎?

該模型也必須正確配置。也許Customer.Address1未映射到數據庫列。在這種情況下,EF將不會檢測到對列的更改。

+0

謝謝Jørgen。 Configuration.AutoDetectChangesEnabled不是DataServiceContext的屬性。我的上下文被設置爲進行更改跟蹤,這在我修改數據並調用SaveChanges時起作用。我只使用上下文的一次實例。所有映射都很好,正如我所說的代碼正在讀取和寫入數據一樣,我自己也無法檢測到這些更改。 –

0

我懷疑客戶端的datacontext是不一樣的,所以變化始終是false

您必須確定Datacontext是同一個(實例),對Datacontext的每個更改。然後檢測這些變化是有意義的。

另一種方法是,您必須自己跟蹤更改,只需使用Trackable Entities來幫助您跟蹤數據上下文中實體的更改。

順便說一句。我使用代碼'ctx.ChangeTracker.HasChanges()'來檢測DataContext的變化。

public bool IsContextDirty(DataServiceContext ctx) 
    { 
#if DEBUG 
     var changed = ctx.ChangeTracker.Entries().Where(t => t.State != EntityState.Unchanged).ToList(); 
     changed.ForEach(
      (t) => Debug.WriteLine("entity Type:{0}", t.Entity.GetType())); 
#endif 
     return ctx != null && ctx.ChangeTracker.HasChanges(); 
    } 
+0

謝謝huoxudong125。我使用的上下文是用於加載實體的相同實例,當調用SaveChanges()時成功保存更改。我在OData客戶端上創建DataServiceContext,在該場景中'ChangeTracker'不是DataServiceContext的成員。 –