koudenpaのブログ

趣味のブログです。株式会社はてなでWebアプリケーションエンジニアをやっています。職業柄IT関連の記事が多いと思います。

Entity Framework Core 2.0 でASP.NET Core起動時にマイグレーションする

こんな感じの Program.cs にすることでとりあえず実現できた。

ただ、この手法はシングルインスタンスのアプリケーションでの適用に限定しておいた方が良さそうだ。

 class Program
{
    public static void Main(string[] args)
    {
        var host = BuildWebHost(args);

        // 起動時にDBマイグレーションを当てる
        UpdateDatabase(host);

        host.Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseApplicationInsights()
            .UseStartup<Startup>()
            .Build();

    private static void UpdateDatabase(IWebHost host)
    {
        // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1#call-services-from-main
        using (var serviceScope = host.Services.CreateScope())
        {
            var services = serviceScope.ServiceProvider;
            var logger = services.GetRequiredService<ILogger<Program>>();
            try
            {
                var dbContext = services.GetService<ApplicationDbContext>();
                dbContext.Database.OpenConnection();

                logger.LogInformation("Start database migration.");
                // https://docs.microsoft.com/ja-jp/ef/core/managing-schemas/migrations/#apply-migrations-at-runtime
                // Webアプリケーションではあまり推奨されていない様子。
                // 明確なマイグレーションタスクをデプロイパイプラインに含めるのが良いようだ。
                dbContext.Database.Migrate();
                logger.LogInformation("End database migration.");

            }
            catch (Exception ex)
            {
                logger.LogError(ex, "An error occurred.");
            }
        }
    }
}

RDBスキーママイグレーションはシステムの運用に当たってそれなりのテーマだと思う。

CoreではないEntity FrameworkではDB接続の初期化処理を設定できて、そこでマイグレーションを行うようなことができた。

Database.SetInitializer(TContext) メソッド (System.Data.Entity)

が、現行のEntity Framework Coreではそうした機能なないようだった。

そこで、ASP.NET Core + Entity Framework Core のシステムにしばらく(結構長い期間)手作業でマイグレーションを実行していたのだけれど、面倒くさい&忘れたり、忘れなくともデプロイ後マイグレーションするまでの間はアプリケーションが動作しない。

これはあまり良くないのでそのうち手作業なしにマイグレーションが行われるようにしようと思っていたのを試した。

ASP.NET CoreとEntity Framework Coreのドキュメントを当たると以下の様な案内がある。

これを組み合わせれば行けそうだ。

ただし、コードからのマイグレーション実行には注釈がある。

この方法は万人向けではありません。 ローカル データベースを利用するアプリには最適ですが、ほとんどのアプリケーションでは、SQL スクリプトの生成など、より堅牢な展開戦略が必要になります。

要するに、CI/CDのパイプラインでマイグレーションした方がいいよ、ということらしい。

Migrate の実装が以下のような感じなので、アプリケーションが複数のインスタンスで動作している場合など、マイグレーション処理が排他されない雰囲気がある。

EntityFrameworkCore/Migrator.cs at release/2.0 · aspnet/EntityFrameworkCore · GitHub

とは言え、自分のようにパイプラインを設けず、Azure App Serviceのデプロイオプションでアプリケーションのデプロイのみを自動化している場合には、起動時にマイグレーションできると便利なのだ。

そんなわけで、先に張り付けたような形でアプリケーションの起動時に適用されるようにした。

これで済まなくなったら Azure DevOps でのマイグレーションを検討しようと思う。