今さらながらですが、ASP.NET MVC ベースの Web アプリケーションを開発する際、Entity Framework の Code First の機能を使って SQL Server データベースを生成してみましたので、その話を備忘録として書いておきます。

基本的には @IT の記事「第2回 Entity Frameworkコード・ファーストでモデル開発」に書いてあった通りの方法で作成してみました。
ただし、環境は MVC3 ⇒ MVC4、EF4 ⇒ EF6、SQL Server Compact 4.0 ⇒ SQL Server 2008 Express と異なります。
また、作成するテーブルも少し簡略化しました。(もちろん @IT の記事の通りに SQL Server にテーブルを作成できます。簡略化したのは、後日、今回作成したデータベースを使って別の記事を書くためです)
Visual Studio 2010 への MVC4 のインストール方法、ベースとなる MVC4 のインターネットアプリケーションの説明については以下のページを見てください。
Entity Framework Code First の機能を使えば、結果として生成されるデータベースのスキーマ等は考える必要はなくなるというような話を聞きますが、個人的には、そんなことはないと思ってます。
単一のテーブルならそうかもしれませんが、今回説明する例のように、Code First の機能によって 2 つのテーブルを生成し、それに外部キー制約がついているような場合は 階層更新 を考えなければなりません。
まぁ、それは次の記事の話として書くとして、今回は Code First の機能を利用して、外部キー制約のある 2 つのテーブルを作るところまでを書きます。
Model
まず、テーブル生成の大元になる Model ですが、以下の例を考えます。親子関係にある Parent クラス と Child クラスおよび ParentChildContext クラスに注目してください。ParentChildInitializer クラスは初期化のためのもの(イニシャライザ)です。
ParentChildContext クラスに設定されている接続文字列名 "DefaultConnection" は、Visual Studio の「MVC4 のインターネットアプリケーション」テンプレートでアプリケーションを作成すると自動的に web.config に設定されるものです。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Web.Mvc;
using System.Data.Entity;
namespace Mvc4App2.Models
{
public class ParentChildInitializer :
DropCreateDatabaseIfModelChanges<ParentChildContext>
{
protected override void Seed(ParentChildContext context)
{
// とりあえずレコードを 2 つ作成してみた。
// Id は IDENTITY になるので設定不用。
var parents = new List<Parent> {
new Parent { Name = "親の名1" },
new Parent { Name = "親の名2" },
};
parents.ForEach(p => context.Parents.Add(p));
context.SaveChanges();
}
}
public class ParentChildContext : DbContext
{
// 接続文字列名 DefaultConnection を指定。
// 指定しないとクラス名 ParentChildContext
// で探しにいくので注意。
public ParentChildContext()
: base("DefaultConnection")
{
}
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Children { get; set; }
}
public class Parent
{
public Parent()
{
Children = new List<Child>();
}
public int Id { get; set; }
[Required(ErrorMessage = "{0} は必須")]
[StringLength(5, ErrorMessage = "{0} は {1} 文字以内")]
[Display(Name = "Parent Name")]
public string Name { get; set; }
public virtual IList<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
[Required(ErrorMessage = "{0} は必須")]
[StringLength(5, ErrorMessage = "{0} は {1} 文字以内")]
[Display(Name = "Child Name")]
public string Name { get; set; }
}
}
ParentChildContext クラスに定義されている Parents, Children プロパティが DB 上のテーブルに、Parent クラス と Child クラスがそれぞれのテーブル内のレコードに該当します。
Parent, Child クラスに定義されている Id という名前のプロパティが DB 上のテーブルでは主キーとして設定されます。int 型の場合は自動的に IDENTITY になりますので注意してください。
また、Name という名前のプロパティは、プロパティに付与した型 string と属性 Required(...), StringLength(5, ...) の設定により、DB のテーブルには Name という名前の nvarchar(5)、NULL 不許可のフィールドとして生成されます。 (参考にした @IT の記事に "null を許容する値型の列は、null 許容型で指定する" と書いてあったんですが、Required(...) 属性の設定が優先されるようです)
特に重要なのが、Parent クラスの中で virtual として宣言されている Chidren プロパティです。
これは「ナビゲーションプロパティ」と呼ばれるもので、上に紹介した @IT の記事によると "エンティティ上で virtual キーワードの付いたプロパティは、それが遅延ロードされることを表す。つまり、明示的に該当するプロパティにアクセスするまで、参照先の値はデータベースから取得されない" というものだそうです。
逆に言えば、この記事の例では、プログラムで Parent クラスのナビゲーションプロパティ Children にアクセスすれば、データベースの Children テーブルから関連するレコードを取得できるということになります。
上記の Model をベースに Entity Framework Code First の機能を利用して SQL Server のデータベースに生成されるテーブルの構成は以下の画像(SQL Server Management Studio で見たもの)に示す通りです。

aspnet-Mvc4App2-20140721201705 というデータベースは、Visual Studio の「MVC4 のインターネットアプリケーション」テンプレートでアプリケーションを作成して、ユーザー登録すると自動的に作成されるものです。(開発環境に SQL Server Express がインストールされている場合)
Children テーブルの Parent_Id という外部キーフィールドは、Parent クラスに Children というナビゲーションプロパティを定義したことによって、フレームワークが自動的に作った NULL を許容する外部キーフィールドです。Parents テーブルの Id フィールドと外部キー制約を持っています。
この記事の Child クラスには外部キープロパティを定義していませんが、Microsoft の文書「Code First 規約」の「リレーションシップ規約」セクションには "型には、ナビゲーションプロパティに加え、依存オブジェクトを表す外部キーのプロパティを追加することをお勧めします" と書いてあります。理由は、連鎖削除を設定するか否かをコントロールするためのようです。
2016/9/12 追記:
Microsoft の文書「Code First 規約」に従って、Child クラスにナビゲーションプロパティと外部キープロパティを定義するとどのような影響があるかを別の記事「
Code First で外部キープロパティの定義」に書きました。外部キープロパティを int 型にしたので、外部キーフィールドはこの記事と違って NULL 不可になります。詳しくは記事を見てください。
「親の名1」「親の名2」という 2 つのフィールドは、ParentChildInitializer クラスの Seed メソッドにより初期値として設定されたものです。
Global.asax
イニシャライザ ParentChildInitializer クラスは、Global.asax の Application_Start メソッドでアプリケーションに登録します。具体例は以下の通りです。
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
// ・・・中略・・・
Database.SetInitializer<ParentChildContext>(
new ParentChildInitializer());
}
}
ここまで済んだら、Visual Studio の[ビルド(B)]⇒[ソリューションのビルド(B)]でソリューションをビルドしておきましょう。そうしておかないと、以降の手順でモデル・クラスなどが認識されません。
実は、ここまででは、まだ SQL Server の DB にはテーブルは作成されていません。Controller と View を作って、Controller のアクションメソッドを呼び出す必要があります。
Controller と View はスキャフォールディング機能で自動的に生成できます(詳しくは上に紹介した @IT の記事を見てください)。以下に、一覧を表示する Index の部分のみ書いておきます。
Controller
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Mvc4App2.Models;
namespace Mvc4App2.Controllers
{
public class ParentChildController : Controller
{
private ParentChildContext db = new ParentChildContext();
//
// GET: /ParentChild/
public ActionResult Index()
{
return View(db.Parents.ToList());
}
// ・・・中略・・・
}
}
View
@model IEnumerable<Mvc4App2.Models.Parent>
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
@Html.DisplayNameFor(model => model.Id)
</th>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Id)
</td>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.ActionLink("Edit", "Edit",
new { id=item.Id }) |
@Html.ActionLink("Details", "Details",
new { id=item.Id }) |
@Html.ActionLink("Delete", "Delete",
new { id=item.Id })
</td>
</tr>
}
</table>
一番上の画像が、上記の Controller と View を使って表示したものです。