2017-11-09 12 views
2

データベースを作成する前にマイグレーションツール"dotnet ef database update"が私のアプリケーションBuildWebHostを実行していることを前提に、マイグレーションを使用している間にデータベースにアクセスする必要のあるサービスを構築するにはどうすればよいですか?データベースが必要なサービスを構成しようとすると、データベースが存在せず、移行コマンドが失敗するため、例外がスローされます。したがって、データベースは決して作成されません。データベースを必要とする移行とサービスがありますか?

私は空のデータベースで"dotnet ef database update"を実行し、より具体的にはasp.netコア2とEFコア2

を使用していると、次のエラーで失敗します。私が構築しているので、

An error occurred while calling method 'BuildWebHost' on class 'Program'.Continuing without the application service provider. Error: Cannot open database "MyDb" requested by the login. The login failed. Login failed for user 'MYCOMPUTER\MYNAME'.

これが起こりますこのMicrosoft Configuration Tutorial私のProgram.csの通り(カスタムオプションのクラスでそれへの結合の最終的な目標と)私の「MYDB」データベースに裏打ちされたカスタム設定プロバイダは、次のようになります。

public static IWebHost BuildWebHost(string[] args) => 
     WebHost.CreateDefaultBuilder(args) 
      .ConfigureAppConfiguration((builderContext, config) => 
       { 
        var tmpconfig = config.Build(); 
        config.AddMyOptionsConfig(options => options.UseSqlServer(tmpconfig.GetConnectionString("My_Database"))); 
       }) 
      .UseStartup<Startup>() 
      .Build(); 

とAddMyOptionsConfigは最終的に実行されます。データベースがまだ作成されていないので、それがデータベースにアクセスしようとしたときにSQLExceptionをスロー

public class EFConfigProvider : ConfigurationProvider 
    { 
    [...] 

     // Load config data from EF DB. 
     public override void Load() 
     { 
      var builder = new DbContextOptionsBuilder<MyDbContext>(); 
      OptionsAction(builder); 

      using (var dbContext = new MyDbContext(builder.Options)) 
      {  
       // dbContext.Database.EnsureCreated(); // will cause first migration to fail 
       Data = !dbContext.ConfigurationValue.Any() // throws exception 
         ? CreateAndSaveDefaultValues(dbContext) 
         : dbContext.ConfigurationValue.ToDictionary(c => c.Id, c => c.Value); 

を。

ただし、dbContext.Database.EnsureCreated()を使用しようとすると、テーブルがすでに存在するため、最初の移行が失敗します。代わりにdbContext.Database.Migrate()を試してみることを考えましたが、初心者としては、プロダクション環境に意図しない結果が生じる可能性があると私は心配しています。そのため、私はコマンドラインツールを使って移行を制御する方が好きです。

基本的には、データベースを作成する前に、BuildWebHost"dotnet ef database update"がアプリケーション起動を実行していますが、BuildWebHostに追加されたカスタム設定プロバイダは、データベースが既に存在する必要があります。

このジレンマを解決するにはどうすればよいですか?

答えて

0

ある意味では、循環参照を作成しました。 ASP.NETコアプロジェクトでマイグレーションを実行する場合、アプリケーションはマイグレーションを実行するために必要なDbContextをインスタンス化するように初期化されます。これは、EFコアのDbContextには、接続文字列名(または実際の完全な接続文字列)が直接定義されるEFの古い方法とは対照的に、DbContextOptionsインスタンスを注入する必要があるためですコンストラクタ

通常、これは正常に機能しますが、気づいたとおり、アプリケーションの初期化自体には既存のデータベースが必要なため、移行前にこれを実行する方法はありません。結果として、2つのオプションがあります。

  1. 既存のデータベースを必要とする部分を抽象化することができます。それはtry-catchでそれをラップし、例外を飲み込むか、より複雑なものにすることができます。移行ピースはこの特定の機能を必要としないため、そのシナリオでのアプリケーションの初期化から安全に除外できます。

  2. DbContextをマイグレーションする機能を満たすために、コンテキストとエンティティをクラスライブラリに移動してIDesignTimeDbContextFactoryを実装できます。その後、ASP.NETコアプロジェクトではなく、このクラスライブラリに対して移行を実行します。それは、移行を行うためにアプリケーションを初期化しなければならないという問題を回避します。

+0

1は動作しますが、コードが正常に実行されていると、伝搬されるべき例外を飲み込む可能性があります。 – pere57

+0

それは "より複雑な"ものが入ってくるだろう。私はこれを自分で試していないが、HttpContextのようなものを提供する可能性がある。これは実際には "実行中の移行"シナリオと実際には非常に異なる要求に応答します。重要な点は、マイグレーションプロセスの一環としてコードが実行されないようにすることです。 –

+0

私は[このドキュメント](https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/dbcontext-creation)から、2と同じ効果を達成できることを認識しましたが、別のプロジェクトは必要ありませんBuildWebHostの名前を変更します。 IDesignTimeDbContextFactoryは、最も一般的なソリューションのように見えます。私のデータベース接続文字列がユーザーの秘密であり、そのための構成にはIWostingEnvironmentが必要です.IHostingEnvironmentはBuildWebHostの外部では利用できません。私はそれを構成する別の方法があると確信しています。 – pere57

0

BuildWebHost(およびStartup.Configure)は、アプリケーション起動ロジックに使用しないでください。 ASP.NETチームの指針は、代わりにProgram.Mainを使用することです。

+0

サービスとしてカスタムオプションクラスを追加しようとしています。 'BuildWebHost'(と' Startup.ConfigureServices')の外にサービスを追加するにはどうしたらいいですか?私はこれを行う方法を見ることができません。 – pere57

+0

サービスを追加することはできますが、設計時に失敗したコードが実行されないように怠惰にすることはできますか? – bricelam

+0

これは組み込みのドットネットコアサービス 'services.Configure (Configuration);'ですが、私は見てすぐに見ました。私の疑問は、 'IConfigurationBuilder.Build()'が呼び出されるとすぐにデータベースがヒットすることです。私はツーリングがdbContextsを検出する方法が大好きですが、例外的なケースが常にあると思います。 (私が参照しているように、あなたが参照するアプリケーション起動ロジックに関するアドバイスは、[ここ](https://docs.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/# – pere57

0

これまでのところ、私の解決策は、カスタム構成プロバイダをバックアップしているエンティティを個別のdbContextに移動することでした。

このテーブルは、メインコンテキスト(MyDbContext)に基づいているため、必要に応じてMyDbOptionsContext.Database.EnsureCreated()経由で移行に影響を与えずに作成できます。

オプションテーブルは移行に参加しなくなりましたが、変更する可能性は低いため、問題は発生しません。もう1つの欠点は、コマンドラインツールdotnet efのコンテキストを明示的に指定する必要性(2つのコンテキストがあるため)です。

関連する問題