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

.net c# entity-framework entity-framework-6

Question

When running unit tests on the build server after switching to Entity Framework 6, I encounter a problem.

I'm utilizingDropCreateDatabaseIfModelChanges initializer. Whenever I switch it toMigrateDatabaseToLatestVersion Although everything functions, I want to continue using the earlier initializer.

What I'm getting wrong is:

System.InvalidOperationException: System.InvalidOperationException: The model backing the 'AppContext' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269)..

It did change, that much is true, but withDropCreateDatabaseIfModelChanges initializer, it has to be rebuilt. Any thoughts?

In App.config, EF is set up. This is the portion that matters:

<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>
1
15
10/17/2013 3:39:18 PM

Popular Answer

It appears that EF 6.0 adds a new regulation:

"Throw an exception when creating the model if the DbContext is utilizing an Initializer AND Migrations are configured."

This wasn't enforced before to the EF 6 RC and up to that point. The unpleasant part is that the use of a DbMigrationsConfiguration defines the phrase "Migrations are configured." If you implemented Migrations, there doesn't seem to be a method to programmatically disable them in testing.

I came up with a solution somewhat similar to Sebastian Piu's: I needed to remove the Configuration class from my tests but was unable to do so because our main project relies on Migrations. Argh!

This was my previous code:

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

In my test code, when the DbContext was being initialized, I ran into the System.InvalidOperationException. There were no issues executing the application as before because it does not utilize an initializer. That merely invalidated my testing.

The solution is to separate the Initializer and DbMigrationsConfiguration such that only one is visible in a runtime context (which feels more like a workaround for items lacking from EF). I want my application to utilize the DbMigrationsConfiguration and my tests to use the Initializer. If DbContext had an interface, this could be accomplished more neatly, but alas, it only supports IObjectContextAdapter.

I created my DbContext abstract first:

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

I then arrived at two 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);
  }
}

Since MyDbContext and MyTestDbContext are both implementations of IMyDbContext, your current dependency injection arrangement should continue to function without any adjustments. Only Spring.NET was tested.

The derived type that MY DbMigrationsConfiguration implements is NOT used by tests:

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

The type of the initializer was then transferred to the test class type that was derived:

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

I can attest that my tests are successful and that my application (as well as Migrations) continues to function normally.

19
10/20/2013 8:54:32 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow