エンティティをデータベースに接続するコンテキスト・クラス
エンティティが定義できたところで、この何の変哲もないPOCOをデータベースに接続する――橋渡しの役目を果たすのがコンテキスト・クラス(System.Data.Entity.DbContextクラスの派生クラス)だ。
まずは、リスト3で具体的な例を確認してみよう。
using System.Data.Entity;
namespace MvcApp.Models
{
public class MyMvcContext : DbContext
{
public DbSet<Book> Books { get; set; } // Booksテーブル
public DbSet<Review> Reviews { get; set; } // Reviewsテーブル
}
}
|
Imports System.Data.Entity
Public Class MyMvcContext : Inherits DbContext
Public Property Books As DbSet(Of Book) ' Booksテーブル
Public Property Reviews As DbSet(Of Review) ' Reviewsテーブル
End Class
|
|
リスト3 データベースに接続するためのコンテキスト・クラス(上:MyMvcContext.cs、下:MyMvcContext.vb) |
もっとも、橋渡しとはいっても、何ということはない。リスト3のように、エンティティとテーブルとをマッピングするために、DbSet<エンティティ>型のパブリック・プロパティ(名前はエンティティ名の複数形)を定義するだけだ。
これによって、コンテキスト・クラスMyMvcContextのBooks、Reviewsプロパティは、同名のBooks、Reviewsテーブルにアクセスし、その結果をレコード単位にBook、Reviewエンティティのインスタンスに割り当てることになる。先ほども述べたように、テーブルの各列(フィールド)は、それぞれエンティティの対応するプロパティにマッピングされる。
データベース接続文字列の準備
これでモデル側の準備は完了だ。続いて、コンテキスト・クラスがデータベースに接続するための接続文字列を「Web.config」ファイルに定義しておこう。
<connectionStrings>
<add name="MyMvcContext" connectionString="Data Source=|DataDirectory|MyMvc.sdf" providerName="System.Data.SqlServerCe.4.0"/>
……中略……
</connectionStrings>
|
|
リスト4 データベース接続文字列の定義(Web.config) |
Entity Frameworkコード・ファースト固有のポイントは1点、接続名(name属性)の部分である。Entity Frameworkでは、接続名はコンテキスト・クラスの名前(ここでは「MyMvcContext」)と等しくなければならない。Entity Frameworkは、この命名規約によって接続情報を取得しようとするためだ。
なお、本稿ではSQL Server Compact 4.0を前提としているので、プロバイダ名は「System.Data.SqlServerCe.4.0」としているが、従来のSQL Serverに接続するならば、「System.Data.SqlClient」とすればよい。
イニシャライザによるテスト・データの準備
Entity Frameworkでは、モデルの修正が行われた場合に、即座にスキーマの変更をデータベースに反映させ、また、動作確認用のデータを展開するためのイニシャライザという仕組みを提供している。イニシャライザを利用することで、開発者はデータベース側を最後までほとんど意識することなく、モデル(スキーマ)の修正を行えるというわけだ。この機能は、スキーマの変動が激しい開発途上の段階ではとても有効なものだ。
以下では、モデルの変更があった場合にデータベースを再作成(drop & create)し、テスト・データを投入するためのイニシャライザの作成例である。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
namespace MvcApp.Models
{
public class MyMvcInitializer : DropCreateDatabaseIfModelChanges<MyMvcContext>
{
protected override void Seed(MyMvcContext context)
{
var books = new List<Book> {
new Book { Isbn = "978-4-8399-3793-5",
Title = "HTML5基礎",
Price = 2980,
Publish="毎日コミュニケーションズ",
Published = DateTime.Parse("2011-03-25") },
new Book { Isbn = "978-4-7741-4663-8",
Title = "Ruby on Rails 3プログラミング",
Price = 3675, Publish="技術評論社",
Published = DateTime.Parse("2011-05-12") },
……中略……
};
books.ForEach(b => context.Books.Add(b));
context.SaveChanges();
}
}
}
|
Imports System.Data.Entity
Public Class MyMvcInitializer
Inherits DropCreateDatabaseIfModelChanges(Of MyMvcContext)
Protected Overrides Sub Seed(context As MyMvcContext)
Dim books = New List(Of Book)() From {
New Book() With { .Isbn = "978-4-8399-3793-5", _
.Title = "HTML5基礎", _
.Price = 2980, _
.Publish = "毎日コミュニケーションズ", _
.Published = DateTime.Parse("2011-03-25")},
New Book() With { .Isbn = "978-4-7741-4663-8", _
.Title = "Ruby on Rails 3プログラミング", _
.Price = 3675, _
.Publish = "技術評論社", _
.Published = DateTime.Parse("2011-05-12")},
……中略……
}
books.ForEach(Function(b) context.Books.Add(b))
context.SaveChanges()
End Sub
End Class
|
|
リスト5 データベースを初期化するためのイニシャライザ(上:MyMvcInitializer.cs、下:MyMvcInitializer.vb) |
イニシャライザを定義するには、目的に応じて、以下のいずれかのクラス(いずれもSystem.Data.Entity名前空間)を継承すること(リスト5の
)。
クラス |
概要 |
CreateDatabaseIfNotExists<T> |
データベースが存在しない場合のみ生成 |
DropCreateDatabaseIfModelChanges<T> |
モデルが変更された場合のみ再生成 |
DropCreateDatabaseAlways<T> |
アプリケーション起動時に常にデータベースを再生成 |
|
表1 イニシャライザを定義するための基底クラス |
「T」はコンテキスト・クラス。 |
Seedメソッドは、テスト・データを投入するための処理を表すprotectedメソッドだ(リスト5の
)。引数としてコンテキスト・クラスを受け取るので、これを利用して、テーブルにデータを保存すればよい。
リスト5では、投入すべきデータをList<エンティティ>型で準備し、これをForEachメソッドでコンテキストに流し込んだ後、SaveChangesメソッドでデータベースに反映している。Entity Frameworkによるデータの保存/更新については、別稿「Entity Frameworkにおけるクエリと更新」が詳しいので、こちらも併せて参照してほしい。
ここでは取りあえず、Booksテーブルに対してのみデータを投入しているが、(もちろん)同じ要領でReviewsテーブルに対してもデータをセット可能だ。
イニシャライザの準備ができたら、後はGlobal.asaxのApplication_Startメソッドから、アプリケーションに登録するだけだ。
using System.Data.Entity;
using MvcApp.Models;
……中略……
protected void Application_Start()
{
……中略……
RegisterRoutes(RouteTable.Routes);
Database.SetInitializer<MyMvcContext>(new MyMvcInitializer());
}
|
Imports System.Data.Entity
……中略……
Sub Application_Start()
……中略……
RegisterRoutes(RouteTable.Routes);
Database.SetInitializer(Of MyMvcContext)(New MyMvcInitializer())
End Sub
|
|
リスト6 イニシャライザを登録するためのコード(上:Global.asax.cs、下:Global.asax.vb) |
イニシャライザを登録するには、SetInitializerメソッドを呼び出せばよい。
public static void SetInitializer<TContext>(
IDatabaseInitializer<TContext> strategy)
|
|
SetInitializerメソッドの呼び出し |
「TContext」はコンテキストの型、「strategy」はイニシャライザのインスタンス。 |
リスト5の例では、引数strategyに対して先ほど作成したMyMvcInitializerオブジェクトをセットしているが、テスト・データの投入を必要としない(Seedメソッドのオーバライドが不要である)場合には、表1でも示したSystem.Data.Entity名前空間のクラスを直接にセットしても構わない。
例えば、以下のようなコードも可能である。
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyMvcContext>());
|
Database.SetInitializer(new DropCreateDatabaseIfModelChanges(Of MyMvcContext)())
|
|
リスト7 SetInitializerメソッドの記述例(上:C#、下:VB) |
以上、モデル部分のコードを準備できたところで、いったんメニューの[ビルド]−[ソリューションのビルド]からソリューションをビルドしておこう。さもないと、以降の手順でモデル・クラスなどが認識されないので注意されたい。