mvcConf @:Japan ふりかえり 2: CodeFirstでのデータベース定義の概要
CodeFirst ではクラスを定義すればそれがそのままテーブルの設計となります。
データベースにあまり強くないプログラマにとってこれはうれしいことのようですが、実際にはそうでもありません。
どのようにテーブル設計をするのがよいのかよく考えたうえでクラスの定義を行う必要があります。
ただ、そのクラス定義を必要があれば簡単に変更し、すぐにデータベースに反映していくことができる、という意味で、やはりCodeFirstはプログラマにとって大変魅力的なものとなります。
このクラス定義=テーブル設定においては、CoCの原則が適用されます。
たとえば、
- ID、またはクラス名+IDという名前のプロパティは自動的に主キーとなる
- ナビゲーションプロパティを定義すると、それにあわせてリレーションが設定される
- リレーションの相手となるクラス名+IDという名前のプロパティは外部キーとなる
- ナビゲーションプロパティに virtual を指定することでレイジーローディングが有効になる
- 文字列型のプロパティはnull許容の設定になる
- 値型のプロパティはnull否許容となる。nullable型(?をつける)とするとnull許容の設定になる
- getメソッドしかもたないプロパティ項目はデータベース上に作成されず、計算で表示される項目となる
といった規約が利用できます。
これらの規約にたいし、属性を設定することでデータベース上の設定を変更することができます。
たとえば [Required] を付加することで文字列型のプロパティでもnull否許容とすることができます。
また、[MaxLength] により、データベース上の文字の最大値を設定することができます。
属性での設定以上に細かい定義をしたい場合はFluent APIと呼ばれるAPIを利用して設定を行うことになります。
MemberクラスのNameプロパティに [Required] の設定を行うのと同じことをFluent APIで記述すると上記のようになります。
注意が必要なのは、Fluent APIを利用することでデータベース上の定義は変更することができますが、これはASP.NET MVCとしての動作にはそのままでは影響を与えない、という点です。
データベース上でName項目はnull否許容となるので、データの更新時に値が入っていなければエラーとなります。
ただし、[Required] という属性定義を利用した場合、あとで述べるASP.NET MVCのバリデーションの機能が働くため、エラーとなるまえに利用者にメッセージを表示して注意を促すことができます。
なお、Fluent APIを利用すると細かい定義を設定することが可能です。
特にすでに存在しているデータベースとPOCOクラスを関連づけたい、という場合はFluent APIを利用しないといけない場面があるでしょう。
単なるクラス定義を実際にデータベース定義に結びつけるのがDbContextを継承したクラスでのDBSet<T>型のプロパティ定義です。
テーブル名はここで指定されたプロパティの名称になりますが、プロパティ名を単数形で指定してもテーブル名は複数形になるようです。
なお、テーブル名はPOCOクラスに[Table]メソッドを付加することで指定することが可能ですし、Fluent APIを用いて指定することも可能です。
データベースを作成する場所としては、DbContextを継承したクラスの名称と同じ名称が設定されている接続文字列が探されます。
もしそのような接続文字列が存在しない場合、CodeFirstはSQL ServerのSQLExpressインスタンス(VisualStudioをインストールする際、デフォルトでSQL Server Expressのインスタンスとして作成される)の中にデータベースを作成します。
このとき作成されるデータベースの名前はnamespaceを含めたDbContextを継承したクラス名となります。
接続文字列がみつかった場合はその接続文字列に指定されている場所、名前でデータベースが作成されます。
このときSQLCe4を指定することもできます。
デフォルトではCodeFirstによるデータベースの生成は一度しか行われず、定義の変更も行われません。
このため、POCOのクラス定義を変更すると、エラーが発生します。
Initializerの基底となるクラスを変更し、アプリケーションの実行時にInitializerを設定することでこの動作を変更することができます。
Initializerの基底となることができるクラスには以下のものがあります。
- CreateDatabaseIfNotExists
- DropCreateDatabaseIfModelChanges
- DropCreateDatabaseAlways
それぞれの役割は名前のとおりです。
また、InitializerクラスのSeedメソッドをオーバーライドすることでデータベースの初期値を設定することができます。
データベースの作成時に常に同じデータをセットすることができるため、テスト環境の構築には有効だと思われます。
また、マスターデータ等初期状態で必須となるデータをデータベースに設定するのにも使えると思います。
mvcConf @:Japan セッションスライド
mvcConf @:Japan で使われた各セッションのスライドが以下に公開されています。
http://www.slideshare.net/mvcjpn/presentations
動画の公開はまた後日になります。
mvcConf @:Japan ふりかえり 1: MVC3 + CodeFirst デモ
mvcConf @:Japan で「MVCのM」という題でセッションを行いました。
十分に話せなかった点も多いので、何回かにわけてBlog上でセッションを再構成してみたいと思います。
最初にMVC3とEF 4.1 CodeFirstを使ってサンプルアプリケーションを構築します。
前提としてASP.NET MVC 3 Tools Updateが動作する環境を用意してください。
セッション中では説明しませんでしたが、私のデモでは無償で利用できるVisual Web Developer 2010を使用していました。
もちろん VisualStudio 2010でもまったく同じことができます。
1) 新規に ASP.NET MVC 3 Web アプリケーションを作成します。
2) ModelsフォルダにMyDal.csクラス(名前は別でもかまいません)を追加し、下記のコードを記述します。
*実際のセッションではあらかじめ作成してあったcsファイルを追加しました。
—————————————————————————————–
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
namespace MyDal
{
public class Member
{
public int ID { get; set; }
[Required(ErrorMessage="名前は必須入力です")]
public string Name { get; set; }
[RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}", ErrorMessage = "メールが正しくありません")]
public string Mail { get; set; }
public int CategoryID { get; set; }
public DateTime? BirthDay { get; set; }
public int? Age
{
get
{
int? age = null;
if (BirthDay != null)
{
age = DateTime.Now.Year – BirthDay.Value.Year;
if (DateTime.Now < BirthDay.Value.AddDays(-1).AddYears(age.Value)) age -= 1;
}
return age;
}
}
public virtual Category Category { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
public string Name { get; set; }
public virtual ICollection<Member> Members { get; set; }
}
public class GroupContext : DbContext
{
public DbSet<Member> Members { get; set; }
public DbSet<Category> Categories { get; set; }
}
public class GroupInitializer : DropCreateDatabaseIfModelChanges<GroupContext>
{
protected override void Seed(GroupContext context)
{
var categories = new List<Category>
{
new Category{ Name = "仕事"},
new Category{ Name = "オンライン"},
new Category{ Name = "オフライン"},
};
categories.ForEach(s => context.Categories.Add(s));
context.SaveChanges();
}
}
}
—————————————————————————————–
3) web.config にDB接続文字列を追加します。
—————————————————————————————–
<add name="GroupContext" connectionString="Data Source=|DataDirectory|\Group.sdf" providerName="System.Data.SqlServerCe.4.0"/>
—————————————————————————————–
4) Global.asax.cs 内の Application_Start() メソッドに DB 初期化のコードを追加します。
—————————————————————————————-
System.Data.Entity.Database.SetInitializer<MyDal.GroupContext>(new MyDal.GroupInitializer());
—————————————————————————————-
5) ビルドを実行します。
6) Controllers フォルダを右クリックし、「追加」-「コントローラー」を選択します。
7) MemberController を以下の設定で追加(スキャッフォールディング)します。
8 ) MemberController.cs ファイル および Memberの各種ビューが追加されたことを確認します。
9) デバッグを開始し、「http://localhost:ポート番号/Member」 にアクセスします。
10) Create New リンクをクリックします。
11) Mail 項目に「test」とだけ入力し、Createボタンを押してエラー表示を確認します。
12) 項目に適当な値を入力し、Createボタンをクリックしてデータが登録されることを確認します。
13) ソリューションエクスプローラーで「すべてのファイルを表示」し、Group.sdfファイルが作成されていることを確認します。
このとき一度最新の情報に更新する必要があるかもしれません。
14) Group.sdfファイルを右クリックし、「プロジェクトに含める」を選択します。
15) データーベースエクスプローラーで CategoriesテーブルやMembersテーブルが作成されていることを確認します。
ふりかえりの1回目の内容としては、まずはここまで。
サンプルアプリケーションができたら、データの追加/更新/削除ができることを確認してください。