迁移到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>
答案 0 :(得分:19)
好吧,看起来EF 6.0引入了一条新规则:
“如果DbContext正在使用初始化程序并且配置了迁移,则在构建模型时抛出异常”。
直到并包括EF 6 RC,未执行此操作。令人讨厌的部分是“配置迁移”是由DbMigrationsConfiguration的实现定义的。似乎没有办法以编程方式禁用测试中的迁移 - 如果您实现了
我以与Sebastian Piu非常相似的方式解决了这个问题 - 我不得不从我的测试中删除Configuration类,但我不能删除它,因为我们正在为我们的主项目使用Migrations。哎呀!
这是我之前的代码:
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进行分段,因此在运行时环境中只能看到一个。我希望我的测试使用初始化程序,我希望我的应用程序使用DbMigrationsConfiguration。如果DbContext有一个接口,这可以更干净地完成,但是它只实现了IObjectContextAdapter。
首先我制作了DbContext摘要:
public abstract class MyDbContextBase : DbContext, IMyDbContext
{
public IDbSet<Users> Users {get; set;}
public IDbSet<Widgets> Widgets {get; set;}
}
然后我派出了两个班级:
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都是IMyDbContexts,因此您现有的依赖注入设置应该无需更改即可运行。我只测试了Spring.NET。
我的DbMigrationsConfiguration实现了测试不使用的派生类型:
internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
}
最后,初始化程序的类型被移动到派生的测试类类型:
public class TestDatabaseInitializer : DropCreateDatabaseAlways<MyTestDbContext>
{
protected override void Seed(MyTestDbContext context) { }
}
我可以确认我的测试正在通过,我的应用程序(和迁移)仍然像以前一样工作。
答案 1 :(得分:5)
升级到EF6后我发现了同样的问题。在阅读了Stefan的评论并且具有与他描述的相同的症状(测试从我的主项目加载Configuration类)
我的案例中的解决方案/解决方法是
class TestContext: MyDataContext
DropCreateDatabaseAlways<MyDataContext>
更改为DropCreateDatabaseAlways<TestContext>
我可以这样做,因为我的大多数测试只是从PersistenceTest类扩展而来,所以我理解如果你有一个大目录,这可能会很难改变。所以期待其他解决方案
答案 2 :(得分:3)
这是因为您启用了迁移并且正在使用DropCreateDatabaseIfModelChanges初始化程序。 Entityframework不支持将此初始化程序与迁移一起使用。您有两种选择:
或
答案 3 :(得分:2)
看起来这个行为是有意的。以下是其中一位开发人员的引用:
这种行为更改是设计的,因为EF5将创建数据库而不使用已定义的迁移,这意味着初始化程序创建的数据库可能与迁移创建的数据库不同。这可能导致针对一个数据库模式进行测试,但针对不同的数据库模式在生产中运行。但是,我们暂时决定对此行为进行更改,并在此处跟踪:https://entityframework.codeplex.com/workitem/1709
答案 4 :(得分:1)
对我来说很好用的是使用define
排除迁移。方法如下:
Test
的新配置,用于定义TEST
TEST
,则抛出错误。TEST
时排除您的迁移:#if !TEST
internal sealed class Configuration : DbMigrationsConfiguration<Context>
{
//...
}
#endif
您可能需要排除所有迁移,但这些迁移并不完全令人满意(但我还没有尝试过,因为我还没有任何迁移)。