13

我正在使用与 TeamCity、NUnit 和 Git 的持续集成。我最近从 FluentMigrator 迁移(请原谅双关语)到实体框架迁移。我这样做主要是为了利用其中的脚手架功能。

但是,有可能在没有首先将更改搭建到迁移中的情况下签入源代码控制的某些更改(想象一下在提交和推送提交之前应用程序没有运行的场景)。我正在使用预测试的提交工作流,所以我想在预测试中检测到这个问题,而不是等到调用 migrate.exe(这在我的工作流中为时已晚并破坏了“绿色存储库”)。

我的问题是,如何创建一个单元/集成测试来检测迁移模型何时与上下文模型不匹配,以便在尝试迁移之前使构建失败?请注意,我希望它与数据库不匹配,并且不希望从测试中访问数据库。

我尝试了这种方法:

    [Test]
    public void CheckWhetherEntityFrameworkMigrationsContextIsUpToDate()
    {
        Assert.DoesNotThrow(() =>
        {
            CallDatabase();
        });

    }

    private void CallDatabase()
    {
        using (var ctx = new MyContext("SERVER=(local);DATABASE=MyDatabase;Integrated Security=True;"))
        {
            var tenant = (from t in ctx.Tenant
                          select t).FirstOrDefault();
        }
    }

但是,当有待处理的迁移时,这总是会失败(而不仅仅是在迁移模型与上下文模型不同步的情况下,这就是我所追求的)。

更新

针对这个问题,我在 EntityFramework 项目中添加了一个工作项。希望他们会考虑添加一种方法来做到这一点。

4

5 回答 5

11

如果有人觉得这很有用,我会使用以下代码通过针对测试数据库运行所有迁移来测试迁移:

[TestClass]
public class MigrationsTests
{
    [TestMethod]
    public void RunAll()
    {
        var configuration = new Configuration();
        var migrator = new DbMigrator(configuration);
        // back to 0
        migrator.Update("0");
        // up to current
        migrator.Update();
        // back to 0
        migrator.Update("0");
    }
}

这会测试所有迁移,UpDown在关闭自动迁移时检测待处理的更改。

注意:确保您针对测试数据库运行此程序。在我的情况下,测试项目app.config具有到测试数据库的连接字符串(只是一个本地 SQLExpress 实例)。

于 2014-02-03T16:39:32.210 回答
6

这里有一些代码将检查所有模型更改是否已被脚手架到迁移。

bool HasPendingModelChanges()
{
    // NOTE: Using MigratorScriptingDecorator so changes won't be made to the database
    var migrationsConfiguration = new Migrations.Configuration();
    var migrator = new DbMigrator(migrationsConfiguration);
    var scriptingMigrator = new MigratorScriptingDecorator(migrator);

    try
    {
        // NOTE: Using InitialDatabase so history won't be read from the database
        scriptingMigrator.ScriptUpdate(DbMigrator.InitialDatabase, null);
    }
    catch (AutomaticMigrationsDisabledException)
    {
        return true;
    }

    return false;
}
于 2013-10-21T21:16:10.900 回答
5

到目前为止,我想要这里给出的所有答案中最好的,所以这就是我想出的:

[TestClass()]
public class MigrationsTests
{
    [TestMethod()]
    public void MigrationsUpDownTest()
    {
        // Unit tests don't have a DataDirectory by default to store DB in
        AppDomain.CurrentDomain.SetData("DataDirectory", System.IO.Directory.GetCurrentDirectory());

        // Drop and recreate database
        BoxContext db = new BoxContext();
        db.Database.Delete();

        var configuration = new Migrations.Configuration();
        var migrator = new DbMigrator(configuration);

        // Retrieve migrations
        List<string> migrations = new List<string>;
        migrations.AddRange(migrator.GetLocalMigrations());

        try
        {
            for (int index = 0; index < migrations.Count; index++)
            {
                migrator.Update(migrations[index]);
                if (index > 0) {
                    migrator.Update(migrations[index - 1]);
                } else {
                    migrator.Update("0"); //special case to revert initial migration
                }
            }

            migrator.Update(migrations.Last());
        }
        catch (SqlException ex)
        {
            Assert.Fail("Should not have any errors when running migrations up and down: " + ex.Errors[0].Message.ToString());
        }

        // Optional: delete database
        db.Database.Delete();
    }

    [TestMethod()]
    public void PendingModelChangesTest()
    {
        // NOTE: Using MigratorScriptingDecorator so changes won't be made to the database
        var migrationsConfiguration = new Migrations.Configuration();
        var migrator = new DbMigrator(migrationsConfiguration);
        var scriptingMigrator = new MigratorScriptingDecorator(migrator);

        try
        {
            // NOTE: Using InitialDatabase so history won't be read from the database
            scriptingMigrator.ScriptUpdate(DbMigrator.InitialDatabase, null);
        }
        catch (AutomaticMigrationsDisabledException)
        {
            Assert.Fail("Should be no pending model changes/migrations should cover all model changes.");
        }
    }
}

几个值得注意的事情:

  • 您需要在测试项目中安装实体框架
  • 您需要添加到班级[assembly: InternalsVisibleTo("MyTestsProject")]的顶部Configuration
  • 您需要指定一个App.config仅用于测试项目的实体框架连接字符串,因为数据库将被频繁删除和重新创建 - 如果在构建机器上运行请记住并行运行可能会导致冲突,因此您可能希望在每次构建时更改字符串
于 2017-03-07T08:48:00.707 回答
3

我认为这比 Pablo Romeo 的代码效果更好。这会升级然后再次降级以捕获降级脚本中丢失的项目。

[TestFixture]
class MigrationTest
{
    [Test]
    public void RunAll()
    {
        var configuration = new Configuration();
        var migrator = new DbMigrator(configuration);

        // Retrieve migrations
        List<string> migrations = new List<string> {"0"};  // Not sure if "0" is more zero than the first item in list of local migrations
        migrations.AddRange(migrator.GetLocalMigrations());

        migrator.Update(migrations.First());

        // Doe een stapje naar voren, en een stapje terug (www.youtube.com/watch?v=2sg1KAxuWKI)
        // (Dutch pun) meaning: take a small step forward, and a small step back ;)
        for (int index = 0; index < migrations.Count; index++)
        {
            migrator.Update(migrations[index]);
            if (index > 0)
                migrator.Update(migrations[index - 1]);
        }

        migrator.Update(migrations.Last());

        migrator.Update(migrations.First());
    }
}
于 2015-04-26T20:19:14.880 回答
1

EF Core 有一个非常直接的方法:

myDbContext.Database.Migrate();
于 2018-11-06T08:23:10.473 回答