web-dev-qa-db-ja.com

Entity Framework 5の移行:データベースの初期移行と単一シードのセットアップ

私は最近Entity Framework 5にアップグレードしたMVC4アプリを持っていて、各実行を削除および作成する開発スタイルからの移行を使用するようにデータベースを移動しようとしています。

これが、アプリの開始関数で行ったことです。

_protected void Application_Start()
{
    Database.SetInitializer(
        new MigrateDatabaseToLatestVersion< MyContext, Configuration >() );
    ...
}
_

リポジトリプロジェクトで_Enable-Migrations_コマンドを実行しました。これにより初期移行ファイルが作成されると思いましたが、作成されたファイルはConfigurationのみでした。

データベースを削除すると、最初にコードを介して期待どおりにデータベースが作成され、構成ファイルからデータベースがシードされます。構成ファイルで、すべてのAdd()関数をAddOrUpdate()に変更しました

ただし、Webサイトが起動し、すべてのシードデータが何度も複製されるたびに、Configurationファイルのシード関数が実行されます。

私が読んだブログがそれを作成するように提案し、そこにシードデータを置くことができたが、それは_initial migration_ファイルを作成すると想像しました

DBを1回だけシードするようにコードで設定する方法を誰かが説明できますか?


リンク:私がフォローした移行ブログの投稿


これはEF migrate.exeを使用する上で非常に興味深いものですが、移行を実行するために roundhouse を使用するように切り替えました。 EFを使用して、モデルに基づいてマイグレーションを足場しますが、SQLファイルにマイグレーションを書き出すための小さなコンソールアプリを作成しました。次に、ラウンドハウスを使用して、rakeビルドスクリプトを通じて移行自体を実行します。少し多くのプロセスが含まれますが、アプリケーションの起動時にEFを使用してオンザフライで移行を実行するよりもはるかに安定しています。

28
Neil

これは人気のある投稿であることが判明したため、他のユーザーからのフィードバックを考慮して更新しました。知っておくべき主なことは、ConfigurationクラスのSeedメソッドは、アプリケーションが起動するたびに実行されるということです。これは、テンプレートメソッドのコメントが意味するものではありません。誰かの回答を参照してください。 Microsoftにこれ 投稿 理由について-それを見つけてくれたJason Learmouthに感謝。

私のように、保留中の移行がある場合にのみデータベースの更新を実行したい場合は、もう少し作業を行う必要があります。 migrator.GetPendingMigrations()を呼び出すことで保留中の移行があるかどうかを確認できますが、保留中の移行のリストはSeedメソッドが呼び出される前にクリアされるため、ctorで行う必要があります。これを実装するコードは、Migrations.Configurationクラスにあります。

internal sealed class Configuration : DbMigrationsConfiguration<YourDbContext>
{ 
    private readonly bool _pendingMigrations;

    public Configuration()
    {
        // If you want automatic migrations the uncomment the line below.
        //AutomaticMigrationsEnabled = true;
        var migrator = new DbMigrator(this);
        _pendingMigrations = migrator.GetPendingMigrations().Any();
    }

    protected override void Seed(MyDbContext context)
    {
        //Microsoft comment says "This method will be called after migrating to the latest version."
        //However my testing shows that it is called every time the software starts

        //Exit if there aren't any pending migrations
        if (!_pendingMigrations) return;

        //else run your code to seed the database, e.g.
        context.Foos.AddOrUpdate( new Foo { bar = true});
    }
}

シードコードを実際の「アップ」移行コードに含めることを提案している人もいることを指摘しておきます。これは機能しますが、新しい移行のたびにシードコードを配置することを覚えておく必要があることを意味します。ただし、移行のたびにシードが変更される場合は、それが適切な方法である可能性があります。

39
Jon P Smith

移行を手動で追加して、必要なシードコードで埋めることができますか?パッケージマネージャーコンソールで実行:

Add-Migration [Name]

その後、migrationsフォルダーに作成されたファイルを編集できます。

私のプロジェクトでは、コンテキスト構成のSeedメソッドで、Richardのようにシードを実際に行います。実際に優先順位はありません。ただし、アプリケーションがチェックする必要がないため、移行はより効率的です。アプリケーションの起動時にデータベースに行が存在する場合、移行が実行されたかどうかを確認する必要があるだけです。

internal sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
    public Configuration()
    {
        // If you want automatic migrations as well uncomment below.
        // You can use both manual and automatic at the same time, but I don't recommend it.
        //AutomaticMigrationsEnabled = true;
        //AutomaticMigrationDataLossAllowed = true;
    }

    protected override void Seed(MyContext 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.

        context.FontFamilies.AddOrUpdate(
            f => f.Id,
            new FontFamily { Id = 1, PcName = "Arial" },
            new FontFamily { Id = 2, PcName = "Times New Roman" },
        });

私はこれをGlobal.asaxで使用しています:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        // Any migrations that haven't been applied before will
        // automatically be applied on Application Pool restart

        Database.SetInitializer<MyContext>(
            new MigrateDatabaseToLatestVersion<MyContext,
            MyApp.Migrations.Configuration>()
        );
    }
}
6
oldwizard

これも過去に気になったことです。私のデータベースには、Seedイベントで入力される特定のテーブルがあり、Seedメソッド内でそれらの1つが空かどうかを確認するだけです。行がある場合、Seedメソッドは実行されません。間違いありませんが、トリックは実行されます。

2
Richard

this SO question への回答は、なぜSeedがアプリが実行されるたびに実行されるかを説明しています。

私はJon Smithsメソッドを使用していますが、次のような#ifブロックに保留中の移行ステートメントのチェックを入れています。

#if (!DEBUG)
            if (!_pendingMigrations) return;
#endif

そうすることで、Seedメソッドが常に実行されて、シードデータが再入力されます。テスト中に削除を行う場合などに便利ですが、リリース時にパフォーマンスヒットが得られません。

2
Jason Learmouth