こんな感じの 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 でのマイグレーションを検討しようと思う。