web-dev-qa-db-ja.com

DropCreateDatabaseIfModelChanges EF6の結果はSystem.InvalidOperationExceptionになります:コンテキストをサポートするモデルが変更されました

Entity Framework 6に移行した後、ビルドサーバーで単体テストを実行するとエラーが発生します。

DropCreateDatabaseIfModelChanges初期化子を使用しています。 MigrateDatabaseToLatestVersionに変更すると、すべてが機能しますが、以前の初期化子を使用したいと思います。

私が得ているエラーは次のとおりです。

System.InvalidOperationException:System.InvalidOperationException:データベースが作成されてから、「AppContext」コンテキストをサポートするモデルが変更されました。 Code First Migrationsを使用してデータベースを更新することを検討してください( http://go.Microsoft.com/fwlink/?LinkId=238269 )。

どちらが正しいか、変更されましたが、DropCreateDatabaseIfModelChanges初期化子を使用して、再作成する必要があります。何か案は?

EFはApp.configで構成されます。関連する部分は次のとおりです。

<connectionStrings>
    <add name="AppContext" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=my.app.unittest;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
<entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
        <parameters>
            <parameter value="v11.0" />
        </parameters>
    </defaultConnectionFactory>
    <contexts>
        <context type="my.app.core.Data.AppContext, my.app.core">
            <databaseInitializer type="System.Data.Entity.DropCreateDatabaseIfModelChanges`1[[my.app.core.Data.AppContext, my.app.core]], EntityFramework" />
        </context>
    </contexts>
    <providers>
        <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
</entityFramework>
15
Chris

EF6.0では新しいルールが導入されているようです。

「DbContextがイニシャライザーを使用していて、移行が構成されている場合、モデルのビルド時に例外をスローします」。

EF 6 RCまでは、これは強制されませんでした。厄介な部分は、「移行が構成されている」がDbMigrationsConfigurationの実装によって定義されていることです。テストで移行をプログラムで無効にする方法はないようです-実装した場合

Sebastian Piuと非常によく似た方法で回避しました。テストからConfigurationクラスを削除する必要がありましたが、メインプロジェクトに移行を使用しているため、削除することはできませんでした。ああ!

これは以前の私のコードでした:

public class MyDbContext : DbDContext, IMyDbContext
{
  public IDbSet<Users> Users {get; set;}
  public IDbSet<Widgets> Widgets {get; set;}
}

// Migrations are considered configured for MyDbContext because this class implementation exists.
internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
  public Configuration()
  {
    AutomaticMigrationsEnabled = false;
  }
}

// Declaring (and elsewhere registering) this DB initializer of type MyDbContext - but a DbMigrationsConfiguration already exists for that type.
public class TestDatabaseInitializer : DropCreateDatabaseAlways<MyDbContext>
{
    protected override void Seed(MyDbContext context) { }
}

テストコードでDbContextを初期化するときに、System.InvalidOperationExceptionが発生しました。アプリケーションはイニシャライザーを使用しないため、以前のようにアプリの実行に問題はありませんでした。これは私のテストを破っただけです。

解決策(EFに欠けているものの回避策のように感じます)は、InitializerとDbMigrationsConfigurationをセグメント化して、ランタイム環境で1つだけが表示されるようにすることです。テストでInitializerを使用し、アプリケーションでDbMigrationsConfigurationを使用する必要があります。これは、DbContextにインターフェイスがあれば、よりクリーンに実行できますが、残念ながら、IObjectContextAdapterのみを実装します。

まず、DbContextを抽象化しました。

public abstract class MyDbContextBase : DbContext, IMyDbContext
{
      public IDbSet<Users> Users {get; set;}
      public IDbSet<Widgets> Widgets {get; set;}
}

次に、2つのクラスを導出しました。

public class MyDbContext : MyDbContextBase
{
  public MyDbContext(string connectionStringOrName, IDatabaseInitializer<MyDbContext> dbInitializer) 
    : base(connectionStringOrName)
  {
  }
}

public class MyTestDbContext : MyDbContextBase
{
  public MyTestDbContext(string connectionStringOrName, IDatabaseInitializer<MyDbContext> dbInitializer) 
    : base(connectionStringOrName)
  {
    Database.SetInitializer(dbInitializer);
  }
}

MyDbContextとMyTestDbContextはどちらもIMyDbContextであるため、既存の依存性注入の設定は変更を必要とせずに機能するはずです。 Spring.NETのみをテストしました。

私のDbMigrationsConfigurationは、テストで使用されない派生型を実装しています。

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

最後に、初期化子の型が派生テストクラス型に移動されました。

public class TestDatabaseInitializer : DropCreateDatabaseAlways<MyTestDbContext>
{
    protected override void Seed(MyTestDbContext context) { }
}

テストに合格し、アプリケーション(および移行)が以前と同じように機能していることを確認できます。

19
Stefan Mohr

EF6にアップグレードした直後に同じ問題が見つかりました。 Stefanのコメントを読み、彼が説明したのと同じ症状が出た後(テストでは、メインプロジェクトからConfigurationクラスをロードしていました)

私の場合の解決策/回避策は

  • テストプロジェクトで新しいclass TestContext: MyDataContextを作成します
  • イニシャライザーをDropCreateDatabaseAlways<MyDataContext>からDropCreateDatabaseAlways<TestContext>に変更します
  • テスト用のコンテキストを使用するために、実際のコンテキストを作成した場所を更新/一般化します

私のテストのほとんどはPersistenceTestクラスから拡張されているため、これを行うことができます。したがって、大きなカタログがある場合、これを変更するのは面倒かもしれません。だから他の解決策を楽しみにしています

5
Sebastian Piu

移行が有効になっていて、DropCreateDatabaseIfModelChanges初期化子を使用しているため、これはスローされます。 Entityframeworkは、移行でのこの初期化子の使用をサポートしていません。 2つのオプションがあります。

  • イニシャライザを無効にする

または

  • 移行構成を削除して移行を無効にする
3
lukew

この動作は意図されたもののようです。開発者の1人からの引用は次のとおりです。

EF5は定義された移行を使用せずにデータベースを作成するため、この動作の変更は仕様によるものでした。つまり、初期化子によって作成されたデータベースは、移行によって作成されたデータベースとは異なる可能性があります。これにより、1つのデータベーススキーマに対してテストを行うことができますが、別のデータベーススキーマに対して本番環境で実行することができます。ただし、この動作に変更を加えることを暫定的に決定しました。これはここで追跡されています: https://entityframework.codeplex.com/workitem/1709

2
Chris

私にとってうまく機能するのは、defineを使用した移行を除外することです。方法は次のとおりです。

  • Testを定義するTESTという新しい構成を作成します
  • テストでは、TESTが定義されていない場合にエラーをスローします。
  • TESTが定義されている場合は、移行を除外します。
#if !TEST
internal sealed class Configuration : DbMigrationsConfiguration<Context>
{
    //...
}
#endif

すべての移行を除外する必要があるかもしれませんが、これも完全には満足のいくものではありません(ただし、まだ移行がないため、試していません)。

1
jgillich