2013-02-21 13 views
13

コンテキストを使用して直接作業することをお勧めしますか?たとえば、顧客のデータベースがあり、ユーザーが名前で検索し、リストを表示し、そのリストを選択してから、その顧客のプロパティを編集できるとします。Entity FrameworkとWPFのベストプラクティス

お客様のリスト(POCOまたはCustomerViewModelsにマップされている)を取得してすぐにコンテキストを閉じるにはコンテキストを使用する必要があるようです。次に、ユーザーがリスト内のCustomerViewModelsのいずれかを選択すると、UIのカスタマープロパティセクションに値が設定されます。

次に名前、タイプ、ウェブサイトのアドレス、会社のサイズなどを変更できます。保存ボタンを押すと、新しいコンテキストを開き、CustomerViewModelのIDを使用して顧客レコードを取得し、その特性。最後に、私はSaveChanges()に電話し、文脈を閉じます。これはたくさんの仕事です。

私の質問は、それを開いたままのコンテキストで直接作業するのではなく、なぜですか?私は長い生涯のスコープで同じコンテキストを使用して読むことは非常に悪く、必然的に問題を引き起こすでしょう。私の前提は、アプリケーションを1人だけが使用する場合、私はコンテキストを開いてすべてを行うことができます。しかし、多くのユーザーがいる場合は、簡潔な作業単位を維持して、要求ごとにコンテキストを開閉する必要があります。

提案がありますか?ありがとう。


@PGallagher - 完全な答えをありがとう。
@Brice - あなたの入力も参考になります

しかし、@Manos D.の「冗長コードのコメント」コメントは少し気になります。例を見てみましょう。私は顧客をデータベースに保管しており、私の顧客のプロパティの1つはCommunicationMethodです。

[Flags] 
public enum CommunicationMethod 
{ 
    None = 0, 
    Print = 1, 
    Email = 2, 
    Fax = 4 
} 

WPFの管理対象顧客ページのUIには、顧客の通信方法(印刷、電子メール、ファックス)の下に3つのチェックボックスがあります。私はその列挙型に各チェックボックスをバインドすることはできません、それは意味をなさない。また、ユーザーがその顧客をクリックして起きて昼食に行くと、コンテキストが悪い時間にそこに座ります。代わりに、これは私の思考プロセスです。

エンドユーザーがリストから顧客を選択します。コンテキストを新規作成し、その顧客を見つけてCustomerViewModelを返すと、コンテキストが閉じられます(ここでは簡単にリポジトリを残しています)。

using(MyContext ctx = new MyContext()) 
{ 
    CurrentCustomerVM = new CustomerViewModel(ctx.Customers.Find(customerId)); 
} 

今、ユーザーはそれらはまた、保存()メソッドを持っているCustomerViewModel、三のブール値のプロパティにバインドされているとして印刷、電子メール、ファックスボタンのチェックを外す/確認することができます。ここに行く。

public class CustomerViewModel : ViewModelBase 
{ 
    Customer _customer; 

    public CustomerViewModel(Customer customer) 
    { 
     _customer = customer; 
    } 


    public bool CommunicateViaEmail 
    { 
     get { return _customer.CommunicationMethod.HasFlag(CommunicationMethod.Email); } 
     set 
     { 
      if (value == _customer.CommunicationMethod.HasFlag(CommunicationMethod.Email)) return; 

      if (value) 
       _customer.CommunicationMethod |= CommunicationMethod.Email; 
      else 
       _customer.CommunicationMethod &= ~CommunicationMethod.Email; 
     } 
    } 
    public bool CommunicateViaFax 
    { 
     get { return _customer.CommunicationMethod.HasFlag(CommunicationMethod.Fax); } 
     set 
     { 
      if (value == _customer.CommunicationMethod.HasFlag(CommunicationMethod.Fax)) return; 

      if (value) 
       _customer.CommunicationMethod |= CommunicationMethod.Fax; 
      else 
       _customer.CommunicationMethod &= ~CommunicationMethod.Fax; 
     } 
    } 
    public bool CommunicateViaPrint 
    { 
     get { return _customer.CommunicateViaPrint.HasFlag(CommunicationMethod.Print); } 
     set 
     { 
      if (value == _customer.CommunicateViaPrint.HasFlag(CommunicationMethod.Print)) return; 

      if (value) 
       _customer.CommunicateViaPrint |= CommunicationMethod.Print; 
      else 
       _customer.CommunicateViaPrint &= ~CommunicationMethod.Print; 
     } 
    } 

    public void Save() 
    { 
     using (MyContext ctx = new MyContext()) 
     { 
      var toUpdate = ctx.Customers.Find(_customer.Id); 
      toUpdate.CommunicateViaEmail = _customer.CommunicateViaEmail; 
      toUpdate.CommunicateViaFax = _customer.CommunicateViaFax; 
      toUpdate.CommunicateViaPrint = _customer.CommunicateViaPrint; 

      ctx.SaveChanges(); 
     } 
    } 
} 

これは間違っていますか?

答えて

17

長時間実行するコンテキストを使用することは問題ありません。あなたはその意味を知る必要があります。

コンテキストは作業単位を表します。 SaveChangesを呼び出すと、追跡中のエンティティに対する保留中の変更がすべてデータベースに保存されます。このため、各コンテキストの意味を理解する必要があります。たとえば、顧客を管理するタブと製品を管理するタブがある場合、それぞれに1つのコンテキストを使用して、顧客タブの保存をクリックすると、製品に対するすべての変更も保存されません。

コンテキストによって多くのエンティティを追跡すると、DetectChangesの処理速度が低下する可能性があります。これを軽減する1つの方法は、変更追跡プロキシを使用することです。

エンティティをロードすると、エンティティがかなり長くなることができると節約の間の時間ので、楽観的同時実行例外を打つチャンスが短命の状況と比べて大きくなります。これらの例外は、エンティティが読み込みと保存の間で外部的に変更されたときに発生します。 Handling these exceptionsはかなり簡単ですが、それはまだ気づいていることです。あなたがWPFで長寿命のコンテキストで実行できる

一つのクールなことはDbSet.Localプロパティ(例えばcontext.Customers.Local)に結合しています。これは、ObservableCollectionであり、削除対象としてマークされていないすべての追跡対象エンティティを含みます。

これにより、どのアプローチを手助けするかを決めるのに役立つもう少し情報が得られたらと思います。

+0

偉大な答え。あなたの返事をありがとう:EFチームメンバーの答えはここでは常に貴重です。 – JYL

0

コンテキストがデータベースに永続的に接続されていません。基本的には、ディスクからロードしたレコードのインメモリ・キャッシュです。これは、以前にロードされていないレコードを要求したとき、強制的にリフレッシュする場合、または変更をディスクに保存し直した場合にのみ、データベースからレコードを要求します。

、コンテキストを開くレコードをつかん、コンテキストを閉じ、その後ブランド新しいコンテキストからオブジェクトに変更されたプロパティのコピーは、冗長コードのepitomyあります。元のコンテキストだけを残して、それを使ってSaveChanges()を行うことになっています。

あなたは、エンティティフレームワークのバージョンに、「同時実行の取り扱い」についてのGoogle検索を行う必要があり、並行性の問題に対処するために探している場合。私はthisを発見した例として

。コメントに応答して

編集:

だから私はあなたが残りの部分は影響を受けない、レコードの列のサブセットは、新しい値で上書きする必要があります理解して何から?もしそうなら、「新しい」オブジェクトでこれらの少数の列を手動で更新する必要があります。

私はあなたの顧客オブジェクトのすべてのフィールドを反映し、全体の顧客レコードへの編集アクセス権を提供することを意図しているフォームを話していた印象の下にありました。この場合、最終的な結果(年齢に関係なくフォーム値でオーバーライドされたすべてのデータ)が同じになるため、新しいコンテキストを使用してすべてのプロパティを1つずつコピーするのは難しいことです。

+0

私のポストの更新を見て、私がコンテキストを使わずにCustomerViewModelを使用している理由を説明してください。 – BBauer42

3

マイクロソフト参考:

http://msdn.microsoft.com/en-gb/library/cc853327.aspx

彼らは言います。 ( Visual Basicで使用して使用して...エンド)は、ほとんどのケースでのObjectContext

の範囲は、あなたが 使用してステートメント内のObjectContextインスタンスを作成する必要があり

リミット。

これにより、コードがステートメントブロックを終了するときに、オブジェクトコンテキストに関連付けられたリソースが に自動的に配置されるようにすることで、パフォーマンスが向上する可能性があります。

コントロールはオブジェクトコンテキストによって管理されるオブジェクトにバインドされている場合しかし、 のObjectContextインスタンスがあれば、結合が必要 、手動で処分されるように維持されるべきです。詳細については

、オブジェクトサービス(Entity Frameworkの)でリソースの管理を参照してください。 http://msdn.microsoft.com/en-gb/library/bb896325.aspx

長期実行オブジェクトコンテキストでは、不要になったときにコンテキストが になるようにする必要があります。


StackOverflowのリファレンス:このStackOverflowの質問もいくつかの有用な答えを持っている

...いくつかは、あなたがあなたのコンテキストを促進することが示唆されている

Entity Framework Best Practices In Business Logic?

より高水準であり、ここからそれを参照して、単一のコンテキストのみを保持する。


私の10ペンスの価値:usingステートメントでコンテキストをラップ

は、ガベージコレクタがリソースをクリーンアップすることができ、およびメモリリークを防ぐことができます。

明らかに単純なアプリケーションでは、これはほとんど問題ではありませんが、複数の画面があり、すべてが大量のデータを使用している場合は、コンテキストを正しく廃棄しない限り、トラブルに終わる可能性があります。

そこで私は、私は私の新しいまたは変更エンティティに渡し、私は私のリポジトリのそれぞれにAddOrUpdateメソッドを追加しましたあなたが言及している1、、、およびUpdateに同様の方法を採用したりするかどうかに応じて、それを追加していますそれが存在します。


エンティティのプロパティの更新:更新のプロパティについて

は、しかし、私はもう1つのエンティティからすべてのプロパティをコピーするためにリフレクションを使用する単純な関数を使用しました。

Public Shared Function CopyProperties(Of sourceType As {Class, New}, targetType As {Class, New})(ByVal source As sourceType, ByVal target As targetType) As targetType 
    Dim sourceProperties() As PropertyInfo = source.GetType().GetProperties() 
    Dim targetProperties() As PropertyInfo = GetType(targetType).GetProperties() 

    For Each sourceProp As PropertyInfo In sourceProperties 
     For Each targetProp As PropertyInfo In targetProperties 
      If sourceProp.Name <> targetProp.Name Then Continue For 

      ' Only try to set property when able to read the source and write the target 
      ' 
      ' *** Note: We are checking for Entity Types by Checking for the PropertyType to Start with either a Collection or a Member of the Context Namespace! 
      ' 
      If sourceProp.CanRead And _ 
        targetProp.CanWrite Then 
       ' We want to leave System types alone 
       If sourceProp.PropertyType.FullName.StartsWith("System.Collections") Or (sourceProp.PropertyType.IsClass And _ 
         sourceProp.PropertyType.FullName.StartsWith("System.Collections")) Or sourceProp.PropertyType.FullName.StartsWith("MyContextNameSpace.") Then 
        ' 
        ' Do Not Store 
        ' 
       Else 

        Try 

         targetProp.SetValue(target, sourceProp.GetValue(source, Nothing), Nothing) 

        Catch ex As Exception 

        End Try 

       End If 
      End If 

      Exit For 
     Next 
    Next 

    Return target 
End Function 

私は次のようにします。

dbColour = Classes.clsHelpers.CopyProperties(Of Colour, Colour)(RecordToSave, dbColour) 

これにより、もちろん各リポジトリに書き込む必要のあるコード量が削減されます。

+0

徹底的なコメントをありがとう。あなたは水平ルールの下で私が作ったOPへのアップデートに間違ったことを見ますか?再度、感謝します。 – BBauer42

関連する問題