連載:〜ScottGu氏のブログより〜Entity Framework 4“コード・ファースト”:独自のデータベース・スキーマ・マッピングScott Guthrie 著/Chica 訳2010/08/02 |
|
|
先週、新しいEntity Framework 4の“コード・ファースト”開発オプションについてブログ投稿しました。EF“コード・ファースト”オプションは、データの処理に関して、非常に快適なコード中心の開発ワークフローを可能にします。以下のことが可能になります。
- デザイナを開いたり、XMLマッピング・ファイルを定義したりすることなく開発
- ベース・クラスなしの“プレーン・オールド・クラス(plain old classes)”を書くだけでモデル・オブジェクトを定義
- 明示的な設定をすることなく、データベースの永続化が可能な“設定に勝る規約”を使用
先週のブログ投稿では、データベースの永続化を可能にする、デフォルトのEF4マッピング規約の使用方法をお見せしました。これらのデフォルトの規約は新しいアプリケーションで非常にうまく動作し、データベースへ、またはデータベースから、クラスをマップするために何も明示的な設定を行う必要がありません。
今回のブログ投稿では、デフォルトの永続化マッピング・ルールのオーバーライドの方法と、好みの独自データベース・スキーマを使用する方法についてお話します。これは既存のデータベース(スキーマがすでに定義されていて、変更できない可能性のあるもの)がかかわるようなシナリオや、リレーショナル・データベースに保存されているものとは異なるモデルの型にしたい場合のシナリオには特に便利です。
NerdDinnerサンプルの簡単なおさらい
先週のブログ投稿では、簡単な“NerdDinner”アプリケーションを初めから構築するウォークスルーを行い、データを扱うときにEFの“コード・ファースト”により得られる生産性について説明しました。
図1 |
以下はアプリケーション内でデータを表すために作成した2つのモデル・クラスです。これらは“プレーン・オールドCLRオブジェクト”(別名“POCO”)です。
図2 |
そして、“NerdDinners”クラスを作成して、これらのクラスをデータベースへ/データベースからマップできるようにしました。“NerdDinners”はEF“コード・ファースト”ライブラリが提供するDbContextクラスから継承されていて、2つのpublicプロパティを公開しています。
図3 |
データベース保存を可能にするために、EF4“コード・ファースト”規約を使用しました。つまり、“NerdDinners”クラス上の“Dinners”と“RSVPs”のプロパティは、データベースの同じ名前のテーブルにマップします。反対に、“Dinner”と“RSVP”モデル・クラス上の各プロパティは“Dinners”と“RSVPs”テーブルのカラムにマップします。
以下はデータベースにある“Dinners”テーブルに対するデータベース・スキーマの定義です。
図4 |
以下はデータベースにある“RSVPs”テーブルに対するデータベース・スキーマの定義です。
図5 |
EF4の“コード・ファースト”と、このデータベース保存のマッピングを行うために何も設定する必要はありません。これは上記の3つのクラスを書くだけで、デフォルトで可能となります。特別な設定は不要です。
EF4で独自のデータベース永続化マッピングの有効化
EF4“コード・ファースト”では、オプションで、そのデフォルトのデータベース永続化マッピング・ルールをオーバーライドでき、別の方法でクラスをデータベースへマップする方法を設定できます。
これを行う方法はいくつかあります。一番簡単な方法は、DbContextベース・クラス上の“OnModelCreating”メソッドをオーバーライドすることです。
図6 |
上記のOnModelCreatingメソッドは、実行中のアプリケーション内でNerdDinnerクラスが初めて使用されたときに呼び出され、“ModelBuilder”オブジェクトが引数として引き渡されます。ModelBuilderオブジェクトはモデル・オブジェクトのデータベース永続化マッピング・ルールをカスタマイズするのに使用できます。以下でこれを行う方法の例をいくつか見てみましょう。
EFは実行中のアプリケーション内で“OnModelCreating”を一度だけ呼び出します。そして、自動的にModelBuilderの結果をキャッシュします。これによりNerdDinnerクラスがインスタンス化されるたびに、モデル作成が行われないようにします。つまり、アプリケーション内でパフォーマンスを上げるために独自のキャッシュ・ロジックを書く必要はありません。
シナリオ1:テーブル名のカスタマイズ
ではOnModelCreatingメソッドを使用して、モデルのデータベース保存をカスタマイズする方法をいくつか見てみましょう。データベース・スキーマのテーブル名が、マップしたいクラスと異なるモデル・クラスへマップしたいという非常によくあるシナリオをまず見てみます。
例えば、テーブル名に“tbl”プレフィックスを付けるパターンをデータベースが使用していると仮定しましょう。“Dinners”テーブルの代わりに、データベースには“tblDinners”テーブルがあります。
図7 |
“Dinners”モデル・クラスを“tblDinners”テーブルへマップしたいと考えており、そのとき、データ永続化属性を追加することなく行いたいと思っています。
図8 |
NerdDinnerコンテキストクラス内の“OnModelCreating” メソッドをオーバーライドすることで、この独自の保存マッピングを行い、その中では次のように独自のマッピング・ルールを指定できます。
図9 |
上記のOnModelCreatingメソッドにあるコードは、フルーエント(=流れるような)APIデザインを使用しています。これは、より滑らかで可読性のあるコードを作成するために、メソッドの連結を採用したAPI設計のスタイルです。“Dinner”クラスを“tblDinners”テーブルにマップしたいということを示すためにModelBuilderオブジェクトを使用しています。
そしてこれが書く必要のあるコードのすべてです。現在アプリケーションは、Dinnerオブジェクトの検索や保存を行うときは、“Dinners” テーブルの代わりに“tblDinners”テーブルを使用します。これを行うのに、DinnerおよびRSVPモデル・クラスを更新する必要はまったくありません。それらは永続化情報を持たない純粋なPOCOオブジェクトのままです。
上記の変更を試行
以前のブログ投稿で完全なNerdDinnerサンプルをダウンロードしている場合、上記の独自のOnModelCreatingメソッドを追加して修正した後、再実行すると独自のデータベース永続化の実際の動きを確認できます。
以前のブログ投稿では、EF“コード・ファースト”内の自動データベース作成/再作成機能を有効化しました。つまり、上記のOnModelCreatingコードを変更してすぐに、ダウンロードしたNerdDinnerアプリケーションを再実行すると、SQL CEデータベースは“Dinners” テーブルの代わりに“tblDinners” テーブルを使うように更新されることが分かります。更新されると独自のOnModelCreating()マッピング・ルールを優先します。このため、テーブルが“Dinners”の代わりに“tblDinners”になるのです。
最初のブログ投稿の最後で、EFの自動作成データベースを使わなくてもいい方法があるかという質問をいくつか受けました。わたしは、この自動データベース作成/再作成サポートは有効化が必要なオプションである(常に有効ではない)ということを十分明確にできていなかったようです。データベースは(コード、.sql展開スクリプト、SQL管理ツールなどを使用して)、いつでも好きなように明示的に作成できます。その接続文字列をポイントするだけです。その場合、EFはデータベース・スキーマを修正したり作成したりしません。
わたしは最初のブログ投稿で自動データベース作成を説明しましたが、これは新しいプロジェクトの初期段階で活用できる便利な機能だと思ったからです。必ず行わなければならないものではまったくなく、一度も使用しない人も多くいると思います。
重要なのは、ASP.NET MVCアプリケーションのコントローラやビュー内で、いずれのコードも変更する必要がなかったことです。“Dinners”クラスを変更しないので、それらはデータベース保存の変更にまったく影響しません。
シナリオ2:カラム/プロパティ・マッピングのカスタマイズ
データベース・スキーマのテーブルとカラム名が、マップしようとしているクラスおよびプロパティとは異なるものへ、モデル・クラスをマップしたいという、もう1つのよくあるシナリオを見てみましょう。
例えば、“tblDinners”テーブルには“col”がプレフィックスとして付くカラムが存在すると仮定しましょう。それらの名前もすべてDinnerクラスのものとは違います。
図10 |
クリーンな“Dinners”モデル・クラスを“tblDinners”テーブルへマップし、その際にはデータ永続化属性を追加したくないと思っています。
図11 |
この独自の永続化は、次のように“OnModelCreating”メソッドを更新して、少しリッチなマッピング・ルールとして実現できます。
図12 |
上記のコードは前回のシナリオで使用した.MapSingleType()と.ToTable()のフルーエント・メソッドの呼び出しを使用します。違いは、MapSingleTypeメソッドにいくつか追加のカラム・マッピング・ルールも指定している点です。Dinnerクラス上のプロパティとテーブルのカラム名を関連付けた匿名オブジェクトを引き渡すことでこれを行います。
ラムダ式で指定しているdinner引数は強く型付けされたものなので、Visual Studioのコード・エディタ内では“dinner.”プロパティに対するIntelliSenseとコンパイル時チェックを得られます。Visual Studio内でリファクタリングのサポートも受けられます。そのため、いつDinnerクラスのプロパティ名が変更されても、Visual Studioのリファクタリング・サポートを使用して、上記のコンテキスト・メニュー内のマッピング・ルールを自動的に更新できます(手動によるコード手順は不要)。
シナリオ 3: 複数の型にわたりテーブルを分割
データベース内のリレーショナル・テーブルは通常、オブジェクト指向的なモデル・クラスの設計方法とは異なる構造になっています。データベースでは1つの大きなテーブルとして保存されるかもしれないものが、純粋なオブジェクト指向の観点から、複数の関連クラスにわたる方が最善の表現であることがときどきあり、1つのエンティティに関連した複数のオブジェクトとして、テーブルを分割したいことがしばしばあります。
例えば、アドレスに対して1つの“colAddr”カラムを持つ代わりに、“tblDinners”テーブルがイベントの“address”を表現するために複数のカラムを使用していると仮定しましょう。
図13 |
“Dinner”モデル・クラス上に4つの異なるプロパティとして、これらのaddressカラムを公開するよりも、それらを“Address”クラスにカプセル化して、次のように“Dinner”クラスがそれを1つのプロパティとして公開するようにしたいと思います。
図14 |
上記で、単純に4つのpublicプロパティを持つ“Address”クラスを定義し、“Dinner”クラスでパブリックな“Address”プロパティを公開することで、それを参照するようにしているところを確認してください。モデル・クラスは永続化情報のない純粋なPOCOです。
次のようにルールを使用して、データベースの1つのテーブルへの、この階層クラス構造のマッピングをサポートするように、“OnModelCreating”メソッドを更新できます。
図15 |
以前の例と同じマッピング方法を使用していることをご確認ください。そこではテーブルのカラム名を、モデル・オブジェクト上の強く型付けされたプロパティへマップしていました。単純に、その方法を拡張して、複雑なサブ・プロパティもサポートするようにしています。上記における新しい概念は、modelBuilder.ComplexType<Address>メソッドを呼び出して、マッピング式の中で使用できる型としてAddressを登録しているところだけです。
複数のオブジェクトにわたりテーブルを分割できるようにするために書くコードは以上です。
更新されたNerdDinnerサンプルと独自のデータベース永続化ルールをダウンロード
ここで更新されたバージョンのNerdDinnerサンプルをダウンロードできます。VS 2010(または無償のVisual Web Developer 2010 Express)が必要です。
上記のサンプルを動作させるためには、マシン上にSQL CE4のダウンロードとインストールが必要です。EFコード・ファースト・ライブラリはここでダウンロードできます。これらはどちらもマシンには影響しません。
まとめ
“EFコード・ファースト”機能のCTP4リリースは、データを処理するための非常に素晴らしいコード中心の方法を提供します。これにより、多くの生産性と多くのパワーをもたらします。これらの2つのブログ投稿で、それにより提供されるいくつかの可能性が少しでも確認できれば幸いです。
EFコード・ファーストのCTP 4リリースはここからダウンロードできます。“EFコード・ファースト”の詳細は、ADO.NETチームのブログ投稿を確認してください。
- EF CTP4アナウンスのブログ投稿
- EF CTP4生産性改善のブログ投稿
- EF CTP4コード・ファースト・ウォークスルーのブログ投稿
- DataAnnotationsとコード・ファースト
- コード・ファーストとデフォルトの規約
- Scott Hanselman氏のCTP4についてウォークスルー投稿
Hope this helps,
Scott
P.S. ブログに加え、現在Twitterを使って簡単な更新やリンク共有を行っています。twitter.com/scottguで、わたしをフォローしてください。
「〜ScottGu氏のブログより〜」 |
- 第2回 簡潔なコーディングのために (2017/7/26)
ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている - 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう - 第1回 明瞭なコーディングのために (2017/7/19)
C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える - Presentation Translator (2017/7/18)
Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
|
|