更新日: 2010 年 4 月 9 日

Visual Basic の内容はこちらに掲載しています。10 行でズバリ !! 更履歴管理を伴ったエンティティを使用した多層階層システムの構築 (セルフ トラッキング エンティティ) (VB)

このコンテンツのポイント

セルフ トラッキング エンティティを適用した Web サービスを作成しクライアントで利用する方法を理解する

今回紹介するコード

<Service1.svc.cs>

C#
using System.Linq;
using Entity;

namespace Server
{
    public class Service1 : IService1
    {
        public Company getCompany(int CompanyId)
        {
            using (Model1Container db = new Model1Container())
            {
                var result = (from co in db.Companies.Include("Employees")
                          where co.CompanyId == CompanyId
                          select co).FirstOrDefault();
                return result;
            }
        }

        public bool updateCompany(Company c)
        {
            using (Model1Container db = new Model1Container())
            {
                db.Companies.ApplyChanges(c);
                db.SaveChanges();
                return true;
            }
        }
    }
}
 

目次

  1. はじめに
  2. EDM の作成
  3. セルフ トラッキング エンティティの生成
  4. エンティティを別プロジェクトとして再実装
  5. サービスの実装
  6. クライアントの作成
  7. 実行してデータを確認する
  8. おわりに

1. はじめに

システム構成を多層階層に分けて考えた場合、クライアントは直接データベースに接続ができないことが多いと思います。そうした場合、クライアント (プレゼンテーション層) のデータ操作は、中間にあるサーバー (ビジネス層) で実装されたサービスを利用する手法が採用され、保守性、拡張性などを保つことが可能です。

データ アクセス テクノロジーとして、ADO.NET Entity Framework を選択した場合、サービスの実装には ASMX や WCF などのマイクロソフト テクノロジーを用いることが可能ですが、以前のバージョンの ADO.NET Entity Framework ではエンティティを WCF などでシリアル化した場合、クライアントでのデータの変更管理は困難を伴っていました。

ADO.NET Entity Framework 4 では、こうしたシナリオに対応して、セルフ トラッキング エンティティ (自己追跡エンティティ) をシステムで扱うエンティティとして、サービス化した場合 (シリアル化した場合) においてもデータの変更管理を行うことが可能であるため、多層階層でのシステム構築で使用するのに適していると言えます。

ここでは、このセルフ トラッキング エンティティを用いて、簡単な多層階層アプリケーションを作成します。なお、ここで説明する内容を実施するには、Visual Studio 2010 RC 以上が必要になります。現時点 (2010 年 3 月) で、Visual Studio 2010 RC は英語版のみが提供されておりますので、以下の説明ではメニュー名称などが英語表記になります。ご了承ください。

ページのトップへ


2. EDM の作成

EDM を作成するにあたり、新規でプロジェクトを作成します。Visual Studio を起動して、[File] メニューの [New] から [Project] をクリックし、[Add New Project] ダイアログを開きます。そして [Installed Template] で [WCF] を選択し、中央のペインから [WCF Service Application] を選択します。[Name] は [Server] とします。

図 1. プロジェクト テンプレートを選択してプロジェクトを作成

ここで作成したプロジェクトに対して、EDM を追加します。10 行でズバリ !! モデル駆動開発を実現する (モデル ファースト) (C#) を参照して、EDM とデータベースを作成することにします。(10 行でズバリ !! モデル駆動開発を実現する (モデル ファースト) (C#) ではコンソール アプリケーションの EDM を作成していますので、ご注意下さい。それ以外はまったく同様の手法です。) 本来は、データ モデルとして、別のクラス ライブラリー プロジェクトで実装するべきかと思いますが、本記事では実装の簡素化のため、あえてサービスと同一のプロジェクト内に作成します。なお、上記の手順で作成された EDM とデータベースのテーブル スキーマの概略図は下図になります。

図 2. 作成した Entity Data Model

図 3. データベースに作成されたスキーマ

ページのトップへ


3. セルフ トラッキング エンティティの生成

追加された Model1.edmx のプロパティにある [Custom Tool] の値を削除します。この行為により、Model1.edmx が XML 定義によって生成したコード ビハインド クラス (Model1.Designer.cs) が削除されます。この中にはエンティティや Object Services で使用するコンテキスト情報などが含まれます。したがって、この時点で EDM は不完全なものになりますが、この後の手順で、セルフ トラッキング形式のものを作成して適用します。

図 4. Model1.edmx の Custom Tool の値を削除

EDM のデザイナーを右クリックして [Add Code Generation Item...] を選択します。

図 5. Add Code Generation Item…を実行

Add New Item ダイアログが表示されますので、[Installed Template] より [Code] を選択して、中央のペインにある [ADO.NET Self-Tracking Entity Generator] を実行します。[Name] は [Model1.tt] とします。

図 6. ADO.NET Self-Tracking Entity Generator の選択

Model1.Context.tt と、Model1.tt が追加されます。これらのファイルはテンプレートとなっており、実際のエンティティなどを生成します。生成時に Security Warning (下図) が複数回表示されますが、いずれも [OK] を選択してください。

図 7. Security Warning

この結果、プロジェクトは下図のようになります、Model1.tt の配下に各エンティティを定義したクラスなどがあります。(Company.cs など) こうしたクラスの内部で変更管理の機能が実装されています。また、Model1.Context.tt の配下にあるクラスは Object Services で使用するコンテキスト情報などが実装されています。

図 8. Solution Explorer

これでセルフ トラッキング エンティティの実装は完了しているのですが、ここで作成したエンティティは、サーバーと、この後作成するクライアント側で参照する必要があるため、この後の手順で独立したクラス ライブラリー プロジェクト内で再実装していきます。

ページのトップへ


4. エンティティを別プロジェクトとして再実装

上述の理由のため、一度作成したエンティティを別のクラス ライブラリー プロジェクトとして再度実装します。Model1.tt ファイルがエンティティ本体になるので、これを削除しつつ別のクラス ライブラリー プロジェクトに配置する必要があります。

まずは Model1.tt ファイルのプロパティにある [Custom Tool] をクリアします。これによりテンプレート機能をオフにして自動的にクラスが作成される状況を発生しないようにします。(この時点で図 8 にある Model1.cs が削除されます。) しかし、これだけではファイルの削除は完了しないため、次に直接残りのファイルを削除します。

図 9. Model1.tt ファイルのプロパティ

上記のとおり Model1.tt ファイルの配下にあるすべてのファイルを削除します。(Company.cs, Employee.cs, Person.cs) 下図のように選択後右クリックから Delete を実行します。

図 10. Model1.tt 配下のファイルを削除

次に Entity を実装するクラス ライブラリー プロジェクトを新規に追加します。 [File] - [Add] - [New Project] を実行して、[Installed Template] に [Window] を選択します。そして中央のペインより、[Class Library] を選択します。[Name] は、[Entity] とします。

図 11. プロジェクト テンプレートを選択してクラス ライブラリー プロジェクトを作成

追加した Entity プロジェクトに先ほど削除したエンティティを追加します。[Project] メニューの [Add Existing Item] を実行して、[Add Existing Item] ダイアログを開き、Server プロジェクトの配下に移動します。この中にある Model1.tt ファイルを選択して [Add] ボタンの矢印から [Add As Link] を押下します。この時、図 7. Security Warning が表示されますが、[OK] を選択します。

図 12. Model1.tt を Add As Link によって追加

これにより、Model1.tt は、実体としてのファイルは Server プロジェクト配下のフォルダーにありますが、使われるのは Entity プロジェクトの中となります。あえてこのようにする理由は、テンプレート ファイルがエンティティなどを生成するさいに、EDM の定義ファイル (.edmx) と同じ場所である必要があるからです。したがって、実体のファイルの場所の移動はできません。(あるいはテンプレートの内部のコードを修正すれば実体のファイルの移動も可能ですが、リスクを伴うため今回は Add As Link で実装します。) Entity プロジェクトは下図のようになります。

図 13. Entity プロジェクト

更に参照を追加する必要がありますので、[Project] メニューの [Add Reference] を実行して、[Add Reference] ダイアログを開き、System.Runtime.Serialization を追加します。

図 14. Add Reference ダイアログ

これで Entity プロジェクトは完成です。ただし、Model1.tt が移動してしまったために、Server プロジェクトに残された、Model1.Context.tt から生成されるコンテキストがエンティティを発見できなくなります。これを防ぐために、参照の追加を行います。Server プロジェクトで [Project] メニューの [Add Reference] を実行して、[Add Reference] ダイアログを開き、[Projects] タブより [Entity] を追加します。

加えて、Model1.Context.tt のプロパティ [Custom Tool Namespace] に対して Entity と入力してください。これで Model1.Context.tt テンプレートから生成されるコンテキストなどは Entity 名前空間の配下に置かれます。これにより Entity クラス ライブラリーに存在する Company などのエンティティ アクセスがテンプレートの修正なしに可能になります。なお、この時も、図 7. Security Warning が表示されますので、[OK] を選択します。

図 15. Model1.Context.tt のプロパティを変更する

ページのトップへ


5. サービスの実装

ここまでの手順でセルフ トラッキング エンティティを伴ったデータ モデルが完成しましたので、今度はデータ操作を外部に提供するサービスを作成して、その内部の実装で作成したデータ モデルを使用します。

Server プロジェクトに存在する IService1.cs に対して以下のコードを実装し、サービスのインターフェイスを定義します。

C#
using System.ServiceModel;
using Entity;

namespace Server
{
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        Company GetCompany(int CompanyId);
        [OperationContract]
        bool UpdateCompany(Company c);
    }
}
 

今回サービスとして外部に公開するのは、Company を取得する GetCompany と、追加更新削除を担う UpdateCompany です。セルフ トラッキング エンティティではエンティティ自身が変更内容を追跡していますので、保持した複数の変更内容を UpdateCompany 1 回でデータベースに反映することが可能です。これがセルフ トラッキング エンティティの最大のメリットになります。

次にサービスの実体を作成します、Server プロジェクトに存在する Service1.svc.cs に対して以下のコードを実装します。ここでは、コードの簡素さを表現するため、直接ロジックを記述していますが、システム全体の保守性などを考慮する場合は、別途ビジネス ロジック用のクラス ライブラリーの中で実装することを検討してください。また、同様の理由でエラーのハンドリングなども行っておりませんが、本来は更新時のチェックなどを行うことをお勧めします。楽観同時実行の制御なども多層階層ではないパターンと同様に行うことが可能です。

C#
using System.Linq;
using Entity;

namespace Server
{
    public class Service1 : IService1
    {
        public Company GetCompany(int CompanyId)
        {
            using (Model1Container db = new Model1Container())
            {
                var result = (from co in db.Companies.Include("Employees")
                          where co.CompanyId == CompanyId
                          select co).FirstOrDefault();
                return result;
            }
        }

        public bool UpdateCompany(Company c)
        {
            using (Model1Container db = new Model1Container())
            {
                db.Companies.ApplyChanges (c);
                db.SaveChanges();
                return true;
            }
        }
    }
}
 

これで Server プロジェクトが完成しました。最終的なプロジェクトは下図のようになります。

図 16. ここまでのプロジェクト

ページのトップへ


6. クライアントの作成

最後に、サービスを利用するクライアントを作成します。ここではシンプルな実装を紹介するため、コンソール アプリケーションとします。

[File] - [Add] - [New Project] をクリックし、[Add New Project] ダイアログを開きます。そして [Installed Template] で [Windows] を選択し、中央のペインから [Console Application] を選択します。[Name] は任意の名前で結構です。(ここでは [Client] としています)

図 17. プロジェクト テンプレートを選択してプロジェクトを作成

このクライアント プロジェクトにおいても、前で作成した Entity の参照が必要になります。[Project] メニューの [Add Reference] を実行して、[Add Reference] ダイアログを開き、[Projects] タブより [Entity] を追加します。なお、クライアント側において、こうしたエンティティの参照が行えない場合は、WCF Data Services (ADO.NET Data Services) での実装を検討してみてください。

次に作製したサービスの参照を追加します。[Project] メニューの [Add Service Reference] を実行して、[Add Reference] ダイアログを開き、[Discover] ボタンを押します。下図のように、作成したサービスが表示されますので、[Namespace] は既定のまま (ServiceRefrenece1) [OK] を押します。

図 18. Add Service Reference ダイアログよりサービス参照を追加

最後に Program.cs に対して、サービスを呼び出すプログラム コードを記述します。Company と Employee は 1 対多の関係性を持っていますので、Company 型のオブジェクトはグラフ オブジェクトになります。今回は Company の更新と多側の Employee の追加を一度に行い、1 回の UpdateCompany で実行します。

C#
using System;
using System.Linq;
using Client.ServiceReference1;
using Entity;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var service = new Service1Client())
            {
               //Id:1のCompany取得
               Company co = service.GetCompany(1);

               //Id:1の会社が存在しない場合追加
               if (co == null)
               {
                   //Company追加
                   co = new Company();
                   co.CompanyId = 1;
                   co.Name = "テスト会社1";
                   //データベースへ追加
                   service.UpdateCompany(co);
                   //変更なしの状態にクリアする
                   co.MarkAsUnchanged();
               }

                //Company変更
                co.Name = co.Name + "_更新";
               
                //Id:1の社員の件数が0件の場合は追加
                if (co.Employees.Where(x => x.PersonId == 1).Count() == 0)
                {
                    //Employee追加
                    Employee em = new Employee();
                    em.PersonId = 1;
                    em.EmployeeId = 1001;
                    em.Name = "小高太郎";
                    em.Title = "エバンジェリスト";
                    em.Company = co;
                    co.Employees.Add(em);
                }
                //グラフオブジェクトを用いてデータベースへ更新と追加をまとめて行うことが可能
                service.UpdateCompany(co);
            }
            Console.WriteLine("完了しました。");
        }
    }
}
 

ページのトップへ


7. 実行してデータを確認する

Client プロジェクトにおいて [Project] メニューの [Set as StartUp Project] を選択して、Client プロジェクトから開始するようにします。実際にデバッグ実行を行うと下図のようにデータが入力されます。Employee エンティティは Person エンティティを継承しているため、People テーブルにもデータが追加されていることがわかります。

図 19. 実行結果のデータを確認 (SQL Server Management Studio)

ページのトップへ


8. おわりに

ADO.NET Entity Framework に実装された新機能セルフ トラッキング エンティティを用いてシンプルな多層階層アプリケーションを構築しました。本文中にも書いた通り、多層階層でのデータ操作をサービスとして実装する場合、他にも WCF Data Services (ADO.NET Data Services) での実装が考えられます。この実装ではクライアント側にエンティティの参照などは必要ないために各層やレイヤーが明確に分離され、更に変更管理も可能ですが、細かい調整が難しい部分があります。(サービスの切り口、パフォーマンス チューニングなど) ケースによって手法を検討していただくことをお勧めします。


Code Recipe

ページのトップへ