不能在用户事务中使用 DROP DATABASE 语句

S. *_*ter 4 sql-server testing c# unit-test sql-server-localdb

不确定这个问题是否属于这里,但我希望有人能帮助我。

我已经对数据库进行了集成测试(使用 mssql localDB)。我希望每个测试都使用它自己的数据独立运行 - 我想在每个测试运行之前用我的假数据重新植入数据库。我试图用事务来实现它,但没有成功。这是我尝试将其关闭的方法:

public class TestDbInitializer : DropCreateAlways<MyContext>()
{
    public static List<Item> Items;

    public override Seed(DbContext context)
    {
        Items = new List<Item>();

        // Adding items
        // .. 

        Items.ForEach(x => context.Add(x));

        context.SaveChanges();
    }
}


public class BaseTransactionsTests
{
    private TransactionScope _scope

    [TestInitialize]
    public void Initialize()
    {
        _scope = new TransactionScope();
    }

    [TestCleanup]
    public void Cleanup()
    {
        _scope.Dispose();
    }
}

[TestClass]
public class IntegrationTests : BaseTransactionsTests

private IDependenciesContainer _container;

public static void AssemblyInit(TestContext context)
{
    Database.SetInitializer(new TestDbInitializer());

    _container = new DependenciesContainer();

    // Registers all my application's dependencies
    _container.RegisterAll();
}

[TestInitialize]
public void Initialize()
{
    using (var context = new MyContext("TestsDatabase"))
    {
        context.Initialize(true);
    }
}

[TestMethod]
public void TestAddItem()
{
    var controller = _container.Resolve<MyController>();

    var result = controller.AddItem(new Item({Name = "Test"}))

    var goodResult = result as OkNegotiatedResult<string>();

    if (result == null)
        Assert.Fail("Bad result")

    using (var context = new MyContext("TestsDatabase"))
    {
        Assert.AreEqual(context.Items.Count, TestDbInitializer.Items.Count + 1)
    }
}
Run Code Online (Sandbox Code Playgroud)

我在我的测试中使用我的依赖注入器,一次注册所有依赖(AssemblyInitialize)。

我创建了一个用于测试的数据库实例,以及一个带有假数据 Seed 方法的特定 DropCreateAlways 初始值设定项,我也将其设置为 AssemblyInitialize 中的初始值设定项。

我想在每次测试运行之前用我的假数据重新设置数据库。对于这种情况,我实现了包含事务范围的基类。

当我运行我的测试时,在 TestInitialize 中为数据库播种时会引发以下异常:

DROP DATABASE statement cannot be used inside a user transaction
Run Code Online (Sandbox Code Playgroud)

我该如何处理?此外,您如何看待我对这些集成测试的实现?有什么可以改进的?

Han*_*non 6

创建一个单独的初始化程序,如果数据库存在则删除该数据库,然后在任何事务处理代码之外创建数据库。如果由于任何原因而失败,您可以(可能)立即通过测试并调查失败的原因

MSDN

CREATE DATABASE 语句必须在自动提交模式(默认事务管理模式)下运行,并且不允许在显式或隐式事务中使用。

如果需要,您可以为每个单独的测试执行此操作:

  1. DROP DATABASE 如果存在
  2. CREATE DATABASE
  3.  创建必要的 DDL
  4.  运行 DML 单元测试
  5. DROP DATABASE

然而,这太过分了。我只是在所有测试开始时创建数据库,并在最后删除它。每个单元测试都可以包含在回滚或提交的事务中,具体取决于进一步测试的要求。

我寻找了一种资源来解释为什么 CREATE DATABASE不能在事务中使用,但我找不到。Kenneth Fisher提供了以下 TechNet 链接,其中显示了在事务中不起作用的所有命令:

事务中允许的 Transact-SQL 语句

我们的猜测是,其中很多是不允许的,因为它们会影响文件系统,这不是事务性的。