15

迁移到 Entity Framework 6 后,在构建服务器上执行单元测试时出现错误。

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

我得到的错误是:

System.InvalidOperationException:System.InvalidOperationException:支持“AppContext”上下文的模型自创建数据库以来已更改。考虑使用 Code First 迁移来更新数据库 ( 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>
4

5 回答 5

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 进行分段,以便在运行时环境中只看到一个。我希望我的测试使用 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 都是 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) { }
}

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

于 2013-10-20T20:54:32.267 回答
5

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

就我而言,解决方案/解决方法是

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

我可以这样做,因为我的大多数测试只是从 PersistenceTest 类扩展而来,所以我知道如果你有一个大目录,这可能会很痛苦。所以期待其他解决方案

于 2013-10-20T15:02:00.957 回答
3

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

  • 禁用初始化程序

或者

  • 通过删除迁移配置禁用迁移
于 2013-10-18T16:08:51.127 回答
2

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

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

于 2013-10-23T07:52:31.120 回答
1

对我有用的是排除使用define. 方法如下:

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

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

于 2014-02-12T09:12:58.587 回答