2015-10-15 3 views
5

を持っています6.Entity Frameworkの6 - DataServiceContextのは、検出は、私はEntity Frameworkのを実行しているWCFサーバーアプリケーションを持って変更

私のクライアントアプリケーションがたDataServiceContextを介してサーバからのODataを消費し、私のクライアントコードで、私が呼び出すことができるようにしたいですコンテキストの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()を呼び出す前に戻ります。私は場合はコメントアウトした場合ではないctx.HasChanges()は、コードの行を返す

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

は、変更が正常に保存されているので、私は、エンティティが変更を受けたとすることができたことがうれしいですそれを保存。

私が私のコードからその事実を決定することができないだけという、変更あるは、文脈によって追跡なっているようです。

DataServiceContextでHasChangesを特定する方法を教えてもらえますか?

+0

おそらく、私はユースケースを理解していませんが、単にSaveChanges()を呼び出すだけではないのですか?変更がなければ、EFは何もしません。おそらく、EFは内部的に似たようなことをしています。あなたはホイールを再発明しているだけです。 – Vlad274

+0

ありがとうございましたVladさん、実際にデータを保存する前に、「変更を保存してもよろしいですか?」というダイアログをポップアップします。変更がなければ、私はダイアログをポップアップしたくありません。 –

答えて

2

遠い。私はちょうどDataServiceContext.UpdateObjectInternal(entity, failIfNotUnchanged)を読んで、それはUpdateObject(entity)から直接false引数で呼ばれます。

ロジックは次のように読み取ります

すでに変更される場合は
  • 、返します。 (短絡)
  • 変更されていない場合は、failIfNotUnchangedをスローします。 (ChangeState()からのみtrue)
  • これ以外の設定状態は変更されました。その様子でそう

を(データ・チェックが起こっていない)、UpdateObjectはちょうどState列挙/エンティティの内部状態をチェックし、気にしません。これにより、変更がない場合に更新が少し不正確になります。

しかし、私はあなたの問題は、コードのOP第二ブロックに続いている、あなたははUpdateObjectを呼び出す前に、あなたの拡張HasChangesを確認してくださいと思います。あなたのReference.cs(隠しファイルを表示し、次にサービス参照)で読むことができるように、エンティティはほめたためのPOCOです。彼らは明白な特性といくつかの変更を通知する操作をいくつかのOn-持っています。彼らは何をしますかは内部的にはトラック状態です。実際には、エンティティに関連付けられたEntityDescriptorが存在し、EntityTracker.TryGetEntityDescriptor(entity)の状態追跡を担当しています。

ボトムラインは、操作は実際には非常に簡単に仕事、そして私はあなただけの私たちが今知っているように、これは常にレポートHasChanges ==真の意志ので、あなたものの

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

のようにあなたのコードを作成する必要があると思うです小切手をスキップすることもできます。

しかし、絶望しないでください!あなたのサービス参照によって提供される部分クラスは、あなたが望むものとまったく同じように拡張することができます。それはまったく定型のコードなので、.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 **という名前のデータベース列を持つと信じます。プロパティを使用する代わりに、別個のゲッターとセッターメソッドでプライベート変数を使用することになりました。乾杯。 –

2

エンティティを正しく追加/編集していない可能性はありますか? MSAddObjectUpdateObject、またはDeleteObjectを使用して、クライアントで変更の追跡を開始する必要があります(https://msdn.microsoft.com/en-us/library/gg602811(v=vs.110).aspx - の同時処理の管理を参照)。さもなければあなたの拡張方法はよく見えます。

+0

Thanks Todd。私のコンテキスト内のオブジェクトは、クライアント上でアクセスされる前に、ODataを介してコンテキストにロードされます。 '(ctx.Customersのcから、c.CustomerId == CustomerIdがcを選択)の行に沿った何か.FirstOrDefault();'デフォルトで残されたコンテキストマージオプションは、変更追跡を有効にします。変更はデータベースに正常に返されています。変更が自分ではわかりません。 –

+0

Attach()する必要がありますか? –

+0

私は前に 'Attach()'メソッドを見たことがありませんでしたが、その提案のおかげで、接続されていないエンティティをコンテキストに入れて挿入するのではなく更新できるように見えます。私の主体は間違いなく文脈にあり、追跡され、うまく更新されています。 –

0

これを有効にするには、自動変更トラッキングを有効にする必要があります。あなたは

ctx.Configuration.AutoDetectChangesEnabled 

でこの設定を見つけることができるすべてのエンティティオブジェクトは、コンテキストctxによって追跡されなければなりません。 これは、ctxのメソッドの1つによって返されるか、明示的にコンテキストに追加されなければならないことを意味します。

また、DataServiceContextの同じインスタンスでトラッキングする必要があることを意味します。何とか複数のコンテキストを作成していますか?

モデルも正しく設定する必要があります。おそらくCustomer.Address1はデータベース列にマップされません。その場合、EFは列の変更を検出しません。

+0

ありがとうJørgen。 Configuration.AutoDetectChangesEnabledは、DataServiceContextのプロパティではありません。私のコンテキストは変更追跡を行うように設定されていますが、これはデータを変更してSaveChangesを呼び出すときに機能しています。私は文脈の一度だけインスタンスを使用しています。私がコードを読み書きしていると言っているので、すべてのマッピングがうまくいきます。自分で変更を検出することはできません。 –

0

私は、クライアントのdatacontextが同じものではないとは思っていません.so変更は常にfalseです。

Datacontextが変更されるたびに、Datacontextが同じもの(インスタンス)であることを確認する必要があります。次に、変更を検出することは意味があります。

別の方法として、自分で変更を追跡する必要があります。Trackable Entitiesを使用するだけで、データコンテキスト内のエンティティの変更を追跡するのに役立ちます。

BTW。私はコード '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のメンバーではありません。 –

関連する問題