しばやん雑記

ASP.NET とメイドさんが大好きなフリーランスのプログラマーのブログ

Entity Framework コード・ファースト開発の基本

Entity Framework CTP4 では命名規約を利用するだけで一般的な利用において十分なモデルを作成することが出来ますが、ModelBuilder の Fluent API を利用することでより詳細な永続化マッピングルールを定義することも出来ます。

基本的な部分だけですが規約と Fluent API を使った場合の違いを比べてみます。

プライマリキー

規約

public class Product
{
    // 型名 + Id 名のプロパティが自動的にプライマリキーとして扱われる
    public int ProductId { get; set; }
}

Fluent API

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // プライマリキーとして扱うプロパティを指定
    modelBuilder.Entity<Product>().HasKey(p => p.ProductId);
}

データアノテーションを使うこともできますが、特別な理由がない限りはこの名前付けを守るべきだと思います。

必須

規約

対応なし。

Fluent API

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // Property で指定したプロパティを必須にする
    modelBuilder.Entity<Product>().Property(p => p.Name).IsRequired();
}

必須はデータアノテーションを使えば MVC などで検証をすることはできますが、検証を素通りさせてしまうと問題なく登録できてしまいます。

SQL で言う NOT NULL 制約を付けたいときには Fluent API を使う必要があります。

1対多関係

規約

public class Product
{
    public int ProductId { get; set; }

    // virtual にする必要あり
    public virtual Customer Customer { get; set; }
}

public class Customer
{
    public int CustomerId { get; set; }

    // virtual かつ ICollection<T> を使う
    // 存在しなくても問題なし
    public virtual ICollection<Product> Products { get; set; }
}

Fluent API

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // HasRequired を HasOptional に変更すれば Customer が null でも OK
    modelBuilder.Entity<Product>().HasRequired(p => p.Customer).WithMany();
}

Customer から Products のコレクションを使いたい場合は下のように WithMany でプロパティを指定してあげます。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // HasRequired を HasOptional に変更すれば Customer が null でも OK
    modelBuilder.Entity<Product>().HasRequired(p => p.Customer).WithMany(c => c.Products);
}

基本的な事項なんですが、モデルクラスで別のクラス型のプロパティを使う場合は virtual にしないと動的プロキシが作成されないので、意図した通りに動きません。

外部キーをモデルに追加していませんが、テーブルには自動的に型名 + Id のカラムが作成されているようです。この仕組みのおかげでテーブルを意識せずに操作を行うことが出来ますね。

多対多関係

規約

public class Product
{
    public int ProductId { get; set; }

    // virtual と ICollection<T> を使う
    public virtual ICollection<Tag> Tags { get; set; }
}

public class Tag
{
    public int TagId { get; set; }

    // virtual と ICollection<T> を使う
    public virtual ICollection<Product> Products { get; set; }
}

Fluent API

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // HasMany を WithMany で表現
    modelBuilder.Entity<Product>().HasMany(p => p.Tags).WithMany(t => t.Products);
}

特に何もすることなく、自動的にコレクションを認識してくれるので多対多関係を作ることが出来ます。中間テーブルも自動的に作成されるので、全く存在を意識することなく利用できます。

まとめ

はっきり言って、今までのようにデザイナでちまちま作業したり、テーブルを作成するのに比べると全てを C# で定義することが出来るのでわかりやすいですし、覚えることが少なくて済みます。

Entity Framework は登場した時から多対多関係について紹介されていましたが、デザイナでは少しわかりにくい部分もありました。しかしコードファーストでは ICollection を持つクラス 2 つで表現されていて、非常にシンプルですね。

もっと積極的に使って、Entity Framework 界隈を盛り上げていきましょう!