DropCreateDatabaseIfModelChanges EF6 results in System.InvalidOperationException: The model backing the context has changed

前端 未结 5 1186
后悔当初
后悔当初 2021-02-02 15:43

After migrating to Entity Framework 6 I get an error when executing unit tests on the build server.

I\'m using the DropCreateDatabaseIfModelChanges initiali

相关标签:
5条回答
  • 2021-02-02 15:52

    What works fine for me is excluding the migrations using define. Here is how:

    • Create a new configuration called Test that defines TEST
    • In your tests, throw an error when TEST is not defined.
    • Exclude your migrations when TEST is defined:
    #if !TEST
    internal sealed class Configuration : DbMigrationsConfiguration<Context>
    {
        //...
    }
    #endif
    

    You might need to exclude all of your migrations, which isn't totally satisfying either (but I haven't tried it because I don't have any migrations yet).

    0 讨论(0)
  • 2021-02-02 16:03

    This is throwing because you have migrations enabled and you are using the DropCreateDatabaseIfModelChanges initializer. Entityframework does not support using this initializer with migrations. You have two options:

    • Disable the initializer

    or

    • Disable migrations by removing the migrations configuration
    0 讨论(0)
  • 2021-02-02 16:08

    I found the same issue just after upgrading to EF6. After reading Stefan´s comment and having the same symptoms as he describes (tests were loading the Configuration class from my main project)

    Solution/workaround in my case was to

    • create new class TestContext: MyDataContext in my Tests project
    • change the initialiser from DropCreateDatabaseAlways<MyDataContext> to DropCreateDatabaseAlways<TestContext>
    • update/generalise places where I created my real context to use the test one

    I could do this because most of my tests just extend from a PersistenceTest class, so I understand this might be a pain to change if you have a big catalog. So looking forward to other solutions

    0 讨论(0)
  • 2021-02-02 16:09

    Well, it looks like EF 6.0 introduces a new rule:

    "If the DbContext is using an Initializer AND Migrations are configured, throw an exception when building the model".

    Up to and including the EF 6 RC this wasn't enforced. The annoying part is that "Migrations are configured" is defined by the implementation of a DbMigrationsConfiguration. There doesn't appear to be a way to programmatically disable Migrations in tests - if you implemented

    I worked around it in a way very similar to Sebastian Piu - I had to get rid of the Configuration class from my tests, but I couldn't just remove it because we're using Migrations for our main project. Argh!

    This was my code before:

    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) { }
    }
    

    I encountered the System.InvalidOperationException when the DbContext was being initialized in my test code. Since the application doesn't use any Initializer, there were no problems running the app as before. This only broke my tests.

    The solution (which feels more like a workaround to things missing from EF) is to segment the Initializer and DbMigrationsConfiguration so only one is seen in a runtime environment. I want my tests to use the Initializer and I want my application to use the DbMigrationsConfiguration. This could be done more cleanly if DbContext had an interface, but alas it only implements IObjectContextAdapter.

    First I made my DbContext abstract:

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

    Then I derived 2 classes:

    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);
      }
    }
    

    Both a MyDbContext and a MyTestDbContext are IMyDbContexts, so your existing dependency injection setup should work without requiring changes. I only tested Spring.NET.

    My DbMigrationsConfiguration implements the derived type that is NOT used by tests:

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

    Finally, the initializer's type was moved to the derived test class type:

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

    I can confirm my tests are passing and my application (and Migrations) is still working as before.

    0 讨论(0)
  • 2021-02-02 16:13

    It looks like this behavior was intended. Here's a quote from one of the developers:

    This change of behavior was by design because EF5 would create the database without using the defined migrations meaning that the database created by the initializer could be different to the one created by Migrations. This could lead to testing against one database schema but running in production against a different database schema. However, we have tentatively decided to make a change to this behavior and this is being tracked here: https://entityframework.codeplex.com/workitem/1709

    0 讨论(0)
提交回复
热议问题