WCF RIAサービス。 データ更新。 パート3

WCF RIAサービス。 スタート。 パート1

WCF RIAサービス。 データを受信します。 パート2

WCF RIAサービス。 データ更新。 パート3

WCF RIAサービス。 Model-View-ViewModel(MVVM)パターンの紹介。 パート4



前のレッスンでは、WCF RIAサービスのデータ取得機能を詳しく調べました。 今日は、データを更新するプロセスについて説明しますが、これはより複雑です。



導入部分は、2番目のレッスンで作成されたプロジェクトです。



IQueryable <T>およびExpression Tree Magic



最初のレッスンでは、次のようなドメインサービスを作成しました。



public IQueryable<Task> GetTasks() { return this.ObjectContext.Tasks; }
      
      





仕組みについて少し考えてみると、各メソッド呼び出しでデータベース全体がテーブルから抽出されていることが明らかになります。



ただし、これは一見しただけです。 式ツリーと遅延実行が何であるかを見てみましょう。



だから。 「GetTasks」メソッドが呼び出されるとき、これはデータベースが要求され、データが取得されていることを意味しません。 実際、式ツリーを構築し、それをIQueryableとして返すだけです。IQueryableは、このメソッドによって返されるものを単純に記述します。 この場合、理論的には、タスクテーブル全体を取得できます。 式ツリーは、クライアント側に返されるものも記述します。 クエリは直接実行され、データベースからデータを抽出するプロセスは、式ツリーが提供/説明するコレクションを変更しようとしているときにのみ発生します。 ただし、リクエストが送信された後、受信者が式ツリーを変更することは可能です。これにより、結果的にコレクションを埋める結果も変更されます。



式ツリーを変更する機能は、データ抽出プロセスの直前に存在します。 例:



 TasksDomainContext context = new TasksDomainContext(); taskDataGrid.ItemsSource = context.Tasks; EntityQuery<Task> query = context.GetTasksQuery(); LoadOperation<Task> loadOp = context.Load(query.Where(t=>t.TaskId == 1));
      
      





2行目は、ドメインコンテキストの形成が行われるため、実際にはまだ空のドメインコンテキストを介してタスクコレクションをバインドします。 その後、EntityQueryはコンテキストから外れます。 データベースから直接クエリを実行したり、データを取得したりすることはまだありません。 ただし、EntityQueryを使用すると、サーバー上でメソッドを呼び出した後、データベースへのクエリが生成され、データが抽出される式ツリーを作成できます。 この場合、テーブル全体を取得できます。 そして、「Load」メソッドが呼び出された場合にのみ、「Where」フィルターを含む変更された式ツリーが転送され、リクエストの処理後、「ID」列に値「1」を持つ1行のみが返されます。 サーバー部分への非同期呼び出しが発生し、式ツリーが送信されて抽出が行われます。 ただし、サーバー側でも、要求は既に変更されており、データベースから1行のみが返されます。 このメソッドが呼び出されたときに実行されるSQLクエリを調べるだけで、これを自分で確認できます。 つまり、データベースから複数の行と1つの行を抽出するための個別のメソッドを作成する必要がなくなり、プログラマーの生活が楽になります。



DomainContextキャッシングと変更追跡



仕事の論理は解体された。 しかし、コードの解析に取りかかる前に、WCF RIAサービスの概念のいくつかを整理する必要があります。 上記は、ドメインコンテキストの背後で発生するすべてのものとはほど遠いものです。 たとえば、プロキシ呼び出しがあります。 受け取るエンティティまたはそのコレクションは、クライアント側のドメインコンテキストによってキャッシュされます。 このため、上記の例のように、要求を直接実行する前にItemsSourceをTasksコレクションにバインドすることが可能になります。 このコレクションは現在のコレクションに変更され、UIのデータは、サーバーへの非同期呼び出しの後に回答が来ると自動的に更新されます。 キャッシュに加えて、ドメインコンテキストは、キャッシュされたエンティティへの変更に関する情報を保存するため、変更、削除、または追加が発生したかどうかを常に把握しています。



すでに学んだことすべてに基づいて、オブジェクトを変更するたびにサーバーを呼び出す必要はないと結論付けることができます。 たとえば、オブジェクトへの変更を蓄積し、サーバーパーツを1回呼び出すと、すべての変更が正しく行われ、処理されます。



手順1:ドメインサービスにデータ更新メソッドを追加します。



最初のレッスンでは、ドメインサービスを作成するときに、作成ウィザードを使用しました。 [編集の追加]列の各エンティティの横にあるチェックボックスをオンにすると、CRUD機能を実装する各エンティティに対して自動的に生成されたメソッドが取得されます。







コードは次のようになります。



 public void InsertTask(Task task) { if ((task.EntityState != EntityState.Detached)) { this.ObjectContext.ObjectStateManager.ChangeObjectState(task, EntityState.Added); } else { this.ObjectContext.Tasks.AddObject(task); } } public void UpdateTask(Task currentTask) { this.ObjectContext.Tasks.AttachAsModified(currentTask, this.ChangeSet.GetOriginal(currentTask)); } public void DeleteTask(Task task) { if ((task.EntityState == EntityState.Detached)) { this.ObjectContext.Tasks.Attach(task); } this.ObjectContext.Tasks.DeleteObject(task); }
      
      





これらのメソッドは、対応するエンティティフレームワーク操作の単純なラッパーです。



ステップ2:新しいアクションのためにUIに要素を追加する



新しいタスクを追加し、変更されたコレクションをデータベースに保存する2つのボタンを追加します。



 <Button Name="addTaskButton" Content="Add Task" Click="addTaskButton_Click" .../> <Button Name="saveChangesButton" Content="Save Changes" Click="saveChangesButton_Click" .../>
      
      











ステップ3:新しいタスクを作成し、ドメインコンテキストに追加して、変更を保存します。



新しいボタンの「Click」イベントハンドラーにそれぞれ次のコードを追加します。



 TasksDomainContext context = new TasksDomainContext(); private void addTaskButton_Click(object sender, RoutedEventArgs e) { taskDataGrid.ItemsSource = context.Tasks; context.Load(context.GetTasksQuery()); Task newTask = new Task { TaskName = "Deploy app", Description = "Deploy app to all servers in data center", StartDate = DateTime.Today, EndDate = DateTime.Today + TimeSpan.FromDays(7) }; context.Tasks.Add(newTask); } private void saveChangesButton_Click(object sender, RoutedEventArgs e) { context.SubmitChanges(); }
      
      





最初に、ドメインコンテキストが関連付けられている変数が追加されます。 前述のように、ドメインコンテキストは、アプリケーションのサーバー側で適切なメソッドを呼び出して変更を追跡し、適用できるほど長く存続する必要があります。 したがって、メソッド呼び出しを分離して、オブジェクトに変更を追加し、これらの変更を保存します。



[タスクの追加]ボタンをクリックすると、そのハンドラーはDataGridのItemsSourceを置き換えて、最初のレッスンで接続したDomainDataSourceを置き換えます。 次に、「Load」メソッドが呼び出され、ドメインコンテキストに目的のエンティティが入力されます。

次に、新しいTasksオブジェクトを作成して入力し、Tasksドメインコンテキストコレクションに追加します。 上記のコレクションはINotifyCollectionChangedインターフェイスを実装しているため、これらの変更はすぐにUIに反映されます。 ただし、これらの変更はすべて適用され、表示され、ドメインコンテキストキャッシュに格納されていることに注意してください。 ただし、サーバー側およびデータベースでは変更されていません。 変更を適用するには、アプリケーションの対応するボタンをクリックすると呼び出されるSubmitChangesメソッドを呼び出す必要があります。



「タスクの追加」ボタンをクリックすると、新しいタスクが追加されたことがわかりますが、「TaskId」フィールドの値は常に「0」になります。 ただし、「SubmitChanges」ボタンをクリックすると、しばらくしてから、つまり非同期呼び出しが発生した後、リクエストが実行され、データが更新されて返され、関連するようになります。



非同期ドメインコンテキストAPI



これについてはすでに述べましたが、繰り返します。 ドメインコンテキストAPIの「Load」や「SubmitChanges」などのメソッドは非同期に呼び出されます。 これは、UIが通常配置されている、それらを呼び出すスレッドの作業を禁止しないことを意味します。 「舞台裏」でスレッドプールからスレッドを取得し、バックグラウンドでサーバー呼び出しを行い、呼び出しが完了すると、呼び出しUIスレッドに戻り、エンティティコレクションとUI自体を更新します。



これはすべて簡単で美しいです。 いつ機能するか。 しかし、実際には、軟膏には常にハエがあります。 通信に問題があるか、誰かが誤って接続文字列を台無しにしているか、バックグラウンドで並列化の競合があります。 しかし、考えられるすべてのシナリオにもかかわらず、チャレンジがいつ完了し、次に進む機会を提供する必要があるかを知る必要性はなくなりません。 これを行うには、いくつかの方法があります。操作が完了すると呼び出される戻り値型またはコールバックを使用します。



最初のオプションは、非同期に呼び出されたメソッドからの戻り値を操作することです。 LoadメソッドはLoadOperationを返し、SubmitChangesメソッドはSubmitOperationを返します。 どちらもOperationBaseを継承するため、操作に関する十分な量の情報を提供します。この情報は、操作中または操作の完了後に使用できます。 また、操作の終了時に「完了」イベントをトリガーします。当然、このイベントにサブスクライブする機会があります。 もちろん、さまざまなエラー、さまざまなフラグの操作、およびアプリケーションの作成に使用できるその他の多くの機能を利用できます。



「Completed」イベントにサブスクライブする代わりに、オーバーロードされた「Load」または「SubmitChanges」メソッドの呼び出しを使用して、それぞれActionとActionを返すことができます。 リンクをコールバック関数に渡し、操作が完了すると自動的に呼び出されます。



このチュートリアルのビデオ







ソースコード



githubで



All Articles