しばやん雑記

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

Entity Framework 4.3 Beta 1 の自動マイグレーションを試してみた

Entity Framework 4.3 Beta 1 が昨日公開されました。詳細は ADO.NET team blog を見た方が早いと思います。

EF 4.3 Beta 1 Released - ADO.NET Blog - Site Home - MSDN Blogs

要約してしまうと以下の通り。

  • マイグレーション対応
    • Enable-Migrations / Update-Database コマンドを追加
    • 専用のイニシャライザ
    • XML ドキュメントコメントが完璧に
  • DbContext API のバグ修正
    • GetDatabaseValues のバグ
    • DbSet の名前に Unicode が使えなかったバグ

基本的には

Entity Framework 4.2 + マイグレーション対応 + バグ修正 = Entity Framework 4.3

と思っておけばいい感じがします。ちなみに Enum サポートなどは .NET 4.5 向けに出る Entity Framework 5.0 での対応になるみたいですね。

と、EF 4.3 自体の話はこれぐらいにしておいて、早速マイグレーションを試してみたいと思います。EF 4.3 には以下の二種類のマイグレーション方法が用意されています。

  • Code-Based Migrations
  • Automatic Migrations

結局のところ自動でやるか、手動でやるかの違いしかないみたいですが、Automatic Migrations の場合には Code-Based Migrations も部分的に適用できるみたいなので、殆どの場合は Automatic Migrations でいいんじゃないかと思います。

今回は簡単に試してみたいので Automatic Migrations を使ってみます。

EF 4.3 は NuGet ですでに配布されているので、普通に MVC 3 プロジェクトを作って更新することが出来ます。方法としてはコマンドに -IncludePrerelease スイッチを付けるだけです。

// インストール時
Install-Package EntityFramework -IncludePrerelease

// アップデート時
Update-Package -IncludePrerelease

EF 4.3 を使う準備が出来たので、モデルを用意しました。

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

    public string Name { get; set; }
}

いつものように Product クラスですが、今回はマイグレーションしたいので Name プロパティだけ用意しておきました。登録に必要なコントローラや DbContext やビューはスキャフォールディングで適当に作ってください。

この時点でアプリケーションを動かすと、以下のように自動的にテーブルが生成されます。一応、データの確認も行っておきます。

それでは Product クラスに Summary という string 型のプロパティを追加して、もう一度実行してみます。

当然ですがエラーになってしまいましたね。なのでコードファースト開発ではイニシャライザとして DropCreateDatabaseIfModelChanges などを使って、モデルが変更された場合にはテーブルを作り直すようにしていました。

// モデルが変更された場合にはテーブルを作り直す
Database.SetInitializer(new DropCreateDatabaseAlways<ProductContext>())

しかし、EF 4.3 では MigrateDatabaseToLatestVersion という新しいイニシャライザが追加されました。これを使えば自動的にマイグレーションしてくれるのですが、型パラメータを見ると TMigrationsConfiguration というものが必要になっています。

この TMigrationsConfiguration に指定するクラスは Package Manager Console で簡単に生成することが出来ます。以下のコマンドを入力するだけです。

// マイグレーションを有効にするためのクラスを追加
Enable-Migrations

実行すると Migrations 名前空間が追加されて、中には Configuration という名前のクラスが生成されます。

namespace MvcApplication3.Migrations
{
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Migrations;
    using System.Linq;

    internal sealed class Configuration : DbMigrationsConfiguration<Models.ProductContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }

        protected override void Seed(MvcApplication3.Models.ProductContext context)
        {
            //  This method will be called after migrating to the latest version.

            //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
            //  to avoid creating duplicate seed data. E.g.
            //
            //    context.People.AddOrUpdate(
            //      p => p.FullName,
            //      new Person { FullName = "Andrew Peters" },
            //      new Person { FullName = "Brice Lambson" },
            //      new Person { FullName = "Rowan Miller" }
            //    );
            //
        }
    }
}

パッと見るだけで大体わかると思いますが、AutomaticMigrationsEnabled に true をセットすると Automatic Migrations が有効になります。ってそのまんまの意味です。Seed メソッドもコメントにある通り、AddOrUpdate 拡張メソッドを使って初期データを登録するために使いますが、今回は初期値の必要がないのでそのままにしておきます。

最後に Global.asax にイニシャライザを設定して完了です。

Database.SetInitializer(new MigrateDatabaseToLatestVersion<ProductContext, Configuration>());

それでは実際に確認してみました。

Summary カラムが追加され、データ自体も保持されていることが確認できました。ちなみに今は初期値が null になっていますが Code-Based Migrations を部分的に使うことで、カラムの初期値を指定出来ます。しかし、それはまた今度扱ってみたいと思います。

今回はイニシャライザを指定してマイグレーションを実行時に自動で行うようにしましたが、Update-Database コマンドを実行すると任意のタイミングでマイグレーションを行えます。即座にテーブルに反映したい場合には非常に便利ですね。