Silverlight で EF4 新機能 Self-Tracking Entities を活用
最近、忙しさにかまけて Blog を随分サボっていました。すみません。
書きたいことは山のようにあるのですが、、、
さて、今日は Entity Framework 4 の新機能 Self-Tracking Entities を Silverlight で利用してみましょう。Self-Tracking Entities をご存じない方は私の過去記事 ADO.NET Entity Framework を用いたN階層システムの構築手法 を参考にしてください。
今回作るモノ
DBの検索、更新処理を伴う Silverlight 4 アプリケーション
必要な環境
・Visual Studio 2010/.NET 4
実装
1. VS2010 で新規に Silverlight アプリケーションを作成します。(プロジェクト名は”Jobs”)
2. Silverlight クラスライブライ プロジェクトを追加します。(プロジェクト名は”JobsEntities”)
ここには後々、クライアントサイドで利用する Self-Tracking Entities 作成することになります。
次のようなソリューションになっているはずです。
クライアントサイドプロジェクト "jobs” に”jobsEntities” への参照を追加しておきましょう。
3. サーバーサイド(”jobs.Web”)を作成していきましょう。
jobs.Web に Entity Data Model を追加します。今回はサンプル DB でお馴染みの pubs DB の “employee” と “jobs” テーブルを Entity として追加します。employee と jobs は次のように 1 : N の関連があります。ちなみに、ここでは EntityFramework の複数化機能は On にしておきました。あまり意味はありませんが・・・
また Self-Tracking Entities を作成するのでデフォルトで自動生成される Entity や Context は削除する必要があります。edmx ファイルのプロパティで ”カスタムツール” に設定されている ”EntityModelCodeGenerator” を削除しておきます。そしてデザイナのコンテキストメニューから “コード生成項目の追加” を選択します。
“ADO.NET Self-Tracking Entity ジェネレーター” を選択します。名前は”Model.tt” にしました。
すると、Model.Context.tt (Contextクラス)と Model.tt (Self-Tracking Entity クラス)が生成されましたね。
DBアクセスはこれでOKです。それではサービスを作成しましょう。
今回はクライアントが Silverlight アプリケーションなので “Silverlight 対応 WCF サービス を “jobs.Web” プロジェクトに追加します。サービス名は “jobsService” にしました。
jobsService の実装はこんな感じです。
データの取得 “GetJobs” では Jobs データを全件取得します。加えてemployee も Incloude して取得します。データの更新 “UpdateJobs” ではApplyChanges を実行するだけで、jobs データのみならず、関連する employee データもまとめて 追加、更新、削除を実行してくる優れものです。以前は、追加、更新、削除でインタフェースを分けたりしていましたからね。実装が大変でした。
using System; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Activation; using System.Collections.ObjectModel; namespace jobs.Web { [ServiceContract(Namespace = "")] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class jobsService { [OperationContract] public ObservableCollection<job> GetJobs() { using (var ctx = new pubsEntities()) { return new ObservableCollection<job>( ctx.jobs.Include("employees")); } } [OperationContract] public ObservableCollection<job> UpdateJobs( ObservableCollection<job> jobs) { using (var ctx = new pubsEntities()) { foreach (var c in jobs) { ctx.jobs.ApplyChanges(c); } ctx.SaveChanges(); } return GetJobs(); } } } |
ついでに WCF ではデフォルトで受信できるメッセージサイズが小さいので、少しサイズを大きくしておくとよいでしょう。 Web.config の maxReceivedMessageSize、maxBufferSize を次のように設定します。
<customBinding> <binding name="jobs.Web.jobsService.customBinding0"> <binaryMessageEncoding /> <httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647"/> </binding> </customBinding> |
4. 次はクライアントサイドです。
jobsEntities プロジェクトに Self-Tracking Entities を追加します。既存の項目の追加で jobs.Web の Model.tt を選択します。 “リンクとして追加” することをお忘れなく。
また Entities はサーバーサイドと同じ名前空間に揃えておきたいので、jobsEntities プロジェクト既定の名前空間は “jobs.Web” に変更しておきましょう。こんな感じになりましたね。そうそう、当然 Entity はシリアライズ化してサービス経由でやりとりする DataContract になります。よって System.Runtime.Serialization を参照設定で追加しておく必要がありますね。
それでは jobs プロジェクトにサービス参照を追加します。名前空間は jobsServiceReference にしておきます。プロキシさえ出来てしまえばこっちのモノです。
データをバインドする画面の作り方はいろいろありますが、簡単なのはデータソースウィンドウからドラッグ&ドロップで画面を作成する方法でしょう。私はGUI ベースで次のようなレイアウトにしました。
job desc (コンボボックス)を選択すると関連する employee データ が DataGrid に表示されるようにしたいと思います。また Save ボタンを押すとデータ更新を実行します。
コードは以下の通りです。
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using jobs.jobsServiceReference; using System.Collections.ObjectModel; using jobs.Web; namespace jobs { public partial class MainPage : UserControl { ObservableCollection<job> _jobs; public MainPage() { InitializeComponent(); jobsServiceClient service = new jobsServiceClient(); service.GetJobsCompleted += (object s, GetJobsCompletedEventArgs args) => { SetJobs(args.Result); }; service.GetJobsAsync(); } private void SetJobs(ObservableCollection<job> newJobs) { _jobs = newJobs; job_descComboBox.DataContext = _jobs; } private void job_descComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { job j = (job)job_descComboBox.SelectedItem; employeesDataGrid.ItemsSource = j.employees; } private void SaveBtn_Click(object sender, RoutedEventArgs e) { try { jobsServiceClient service = new jobsServiceClient(); service.UpdateJobsCompleted += (object s, UpdateJobsCompletedEventArgs args) => { MessageBox.Show("The jobs saved successfully."); }; service.UpdateJobsAsync(_jobs); } catch (Exception ex) { MessageBox.Show(string.Format("An error occured while saving the jobs:{0}{1}", Environment.NewLine, ex.Message)); } } } } |
当然ながら Silverlight なので非同期を意識する必要はありますが、Self-Tracking Entities を利用すれば Change Tracking をご自身で実装する必要がなくなります。さらに同時実行制御や 1:N データをまとめて処理することも可能です。便利ですね~。