DropCreateDatabaseIfModelChanges EF6导致System.InvalidOperationException:支持上下文的模型已更改

迁移到Entity Framework 6后,在构建服务器上执行unit testing时出错。

我正在使用DropCreateDatabaseIfModelChanges初始化程序。 当我将其更改为MigrateDatabaseToLatestVersion一切正常,但我想坚持使用前一个初始化程序。

我得到的错误是:

System.InvalidOperationException:System.InvalidOperationException:自创建数据库以来,支持“AppContext”上下文的模型已更改。 考虑使用Code First Migrations更新数据库( http://go.microsoft.com/fwlink/?LinkId=238269 )。

哪个是正确的,它已更改,但使用DropCreateDatabaseIfModelChanges初始值设定项时,应重新创建它。 有任何想法吗?

EF在App.config中配置。 这是相关部分:

                   

好吧,看起来EF 6.0引入了一条新规则:

“如果DbContext正在使用初始化程序并且配置了迁移,则在构建模型时抛出exception”。

直到并包括EF 6 RC,这都没有强制执行。 令人讨厌的部分是“配置迁移”是由DbMigrationsConfiguration的实现定义的。 似乎没有办法以编程方式禁用测试中的迁移 – 如果您已实现

我以类似于Sebastian Piu的方式解决了这个问题 – 我不得不从我的测试中删除Configuration类,但我不能删除它,因为我们正在为我们的主项目使用Migrations。 哎呀!

这是我之前的代码:

 public class MyDbContext : DbDContext, IMyDbContext { public IDbSet Users {get; set;} public IDbSet Widgets {get; set;} } // Migrations are considered configured for MyDbContext because this class implementation exists. internal sealed class Configuration : DbMigrationsConfiguration { 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 { protected override void Seed(MyDbContext context) { } } 

在我的测试代码中初始化DbContext时遇到了System.InvalidOperationException。 由于应用程序不使用任何初始化程序,因此像以前一样运行应用程序没有问题。 这只会打破我的考试。

解决方案(感觉更像是EF缺失的解决方法)是对Initializer和DbMigrationsConfiguration进行分段,因此在运行时环境中只能看到一个。 我希望我的测试使用初始化程序,我希望我的应用程序使用DbMigrationsConfiguration。 如果DbContext有一个接口,这可以更干净地完成,但是它只实现了IObjectContextAdapter。

首先,我制作了我的DbContext摘要:

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

然后我派出了2个类:

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

MyDbContext和MyTestDbContext都是IMyDbContexts,因此您现有的dependency injection设置应该可以在不需要更改的情况下工作。 我只测试了Spring.NET。

我的DbMigrationsConfiguration实现了测试不使用的派生类型:

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

最后,初始化程序的类型被移动到派生的测试类类型:

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

我可以确认我的测试正在通过,我的应用程序(和迁移)仍然像以前一样工作。

升级到EF6后我发现了同样的问题。 在阅读了Stefan的评论并且具有与他描述的相同的症状(测试从我的主项目加载Configuration类)

在我的案例中的解决方案/解决方法是

  • 在我的Tests项目中创建新class TestContext: MyDataContext
  • 将初始化程序从DropCreateDatabaseAlways更改为DropCreateDatabaseAlways
  • 更新/概括我创建真实上下文的位置以使用测试

我可以做到这一点,因为我的大多数测试只是从PersistenceTest类扩展,所以我理解如果你有一个大目录,这可能是一个很难改变。 所以期待其他解决方案

这是因为您启用了迁移并且正在使用DropCreateDatabaseIfModelChanges初始化程序。 Entityframework不支持将此初始化程序与迁移一起使用。 你有两个选择:

  • 禁用初始化程序

要么

  • 通过删除迁移配置来禁用迁移

看起来这个行为是有意的。 以下是其中一位开发人员的引用:

这种行为更改是设计的,因为EF5将创建数据库而不使用已定义的迁移,这意味着初始化程序创建的数据库可能与迁移创建的数据库不同。 这可能导致针对一个数据库模式进行测试,但针对不同的数据库模式在生产中运行。 但是,我们暂时决定对此行为进行更改,并在此处进行跟踪: https : //entityframework.codeplex.com/workitem/1709

什么对我来说很好用的是使用define排除迁移。 方法如下:

  • 创建一个名为Test的新配置,用于定义TEST
  • 在测试中,在未定义TEST时抛出错误。
  • 定义TEST时排除迁移:
 #if !TEST internal sealed class Configuration : DbMigrationsConfiguration { //... } #endif 

您可能需要排除所有迁移,这也不完全令人满意(但我还没有尝试过,因为我还没有任何迁移)。