Silverlight で EF4 新機能 Self-Tracking Entities を活用

最近、忙しさにかまけて Blog を随分サボっていました。すみません。

書きたいことは山のようにあるのですが、、、

 

さて、今日は Entity Framework 4 の新機能 Self-Tracking Entities を Silverlight で利用してみましょう。Self-Tracking Entities をご存じない方は私の過去記事 ADO.NET Entity Framework を用いたN階層システムの構築手法 を参考にしてください。

 

今回作るモノ

DBの検索、更新処理を伴う Silverlight 4 アプリケーション

clip_image001

 

 

必要な環境

・Visual Studio 2010/.NET 4

SQL Server 2005/2008

Silverlight 4 tools

pubs サンプルデーターベース

 

実装

1. VS2010 で新規に Silverlight アプリケーションを作成します。(プロジェクト名は”Jobs”)

 

2. Silverlight クラスライブライ プロジェクトを追加します。(プロジェクト名は”JobsEntities”)

ここには後々、クライアントサイドで利用する Self-Tracking Entities 作成することになります。

clip_image002

 

次のようなソリューションになっているはずです。

クライアントサイドプロジェクト "jobs” に”jobsEntities” への参照を追加しておきましょう。

clip_image003

 

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” を削除しておきます。そしてデザイナのコンテキストメニューから “コード生成項目の追加” を選択します。

clip_image004

 

“ADO.NET Self-Tracking Entity ジェネレーター” を選択します。名前は”Model.tt” にしました。

clip_image005

 

すると、Model.Context.tt (Contextクラス)と Model.tt (Self-Tracking Entity クラス)が生成されましたね。

DBアクセスはこれでOKです。それではサービスを作成しましょう。

今回はクライアントが Silverlight アプリケーションなので “Silverlight 対応 WCF サービス を “jobs.Web” プロジェクトに追加します。サービス名は “jobsService” にしました。

clip_image006

 

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 を選択します。 “リンクとして追加” することをお忘れなく。

clip_image007

 

また Entities はサーバーサイドと同じ名前空間に揃えておきたいので、jobsEntities プロジェクト既定の名前空間は “jobs.Web” に変更しておきましょう。こんな感じになりましたね。そうそう、当然 Entity はシリアライズ化してサービス経由でやりとりする DataContract になります。よって System.Runtime.Serialization を参照設定で追加しておく必要がありますね。

clip_image008

 

それでは jobs プロジェクトにサービス参照を追加します。名前空間は jobsServiceReference にしておきます。プロキシさえ出来てしまえばこっちのモノです。

clip_image009

 

データをバインドする画面の作り方はいろいろありますが、簡単なのはデータソースウィンドウからドラッグ&ドロップで画面を作成する方法でしょう。私はGUI ベースで次のようなレイアウトにしました。

clip_image010

 

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 データをまとめて処理することも可能です。便利ですね~。

clip_image011