当用于数据库单元测试时,TransactionScope会导致TransactionManagerCommunicationException

时间:2011-01-19 11:29:17

标签: c# database unit-testing transactionscope

我想在我的数据库中运行一些存储过程的测试,而不会实际影响数据(或者更准确地说,在测试运行后没有持久的影响)。

经过一番研究后,我想出了在我的Visual Studio 2010测试项目中使用TransactionScope的方法,例如

using( new TransactionScope())
{
    using( SqlConnection connection = new SqlConnection("someConnectionString"))
    {
        connection.Open();
        using( SqlCommand command = new SqlCommand( "some sql", connection ))
        {
            // Do some database stuff...
        }
     }
}

现在这个工作正常,只要我把所有这些都放在一个测试方法中,即当TransactionScope的使用块完成时,我对数据库的所有更改都会自动回滚。

我现在的问题是我想在ClassInitialize中做一些数据库工作,所以我只需要为每个测试类做一次而不是每个测试方法。当我创建一个公共TransactionScope属性并在ClassInitialize方法中为它分配一个TransactionScope实例时,这没关系。一旦我在我的一个测试方法中执行任何与数据库相关的东西,我就会在该方法中遇到TransactionManagerCommunicationException。

我不太明白为什么会出现这种情况,我也想知道我的方法是否存在错误,或者如何在不设置TransactionScope(包括所有设置)的情况下使其工作再次在每种测试方法中进行测试的东西。

修改

下面的代码摘录,我希望这能提供足够的信息:

public TransactionScope Scope { get; set; }

[ClassInitialize]
public static void ClassInitialize( TestContext testContext )
{
    Scope = new TransactionScope();
    // Do some db set up stuff, e.g. create records used for tests etc.
}

[ClassCleanup]
public static void ClassCleanup()
{
    Scope.Dispose();
}

[TestMethod]
public void MyTestMethod()
{
    using( SqlConnection connection = new SqlConnection( "someConnectionString" ) )
    {
        DataTable result = new DataTable();
        using( SqlCommand command = new SqlCommand( "spName", connection ) )
        {
            command.CommandType = CommandType.StoredProcedure;
            using( SqlDataAdapter adapter = new SqlDataAdapter() )
            {
                adapter.SelecteCommand = command;
                // The next line causes the exception to be thrown
                adapter.Fill( result );
            }
        }

        // Assertions against DataTable result
    }
}

例外是


用户代码未处理TransactionManagerCommunicationException 已禁用分布式事务管理器(MSDTC)的网络访问。请使用组件服务管理工具在MSDTC的安全配置中启用DTC以进行网络访问。


我知道我可以尝试更改设置,但我不明白为什么我会先得到例外 - 与单个(测试)方法中的代码相比有什么不同?

提前致谢

最好的问候

-G。

3 个答案:

答案 0 :(得分:4)

您的例外是说未启用MSDTC。我的猜测是,当你单独使用TransactionScope时,它只是创建本地SQL事务 - 不需要DTC。但是,当您通过多个连接共享TransactionScope时,事务将通过DTC“升级”为分布式事务,您可能尚未启用该事务。

尝试在本地计算机和服务器上启用MSDTC上的网络访问。这样做的步骤会有所不同,具体取决于您的操作系统。 Here's how to do it in Win 2003 ServerHere's a link for Win 2008。请注意,您可能还需要通过防火墙启用DTC(在上一个链接中进行了解释......)

答案 1 :(得分:0)

你可以创建这样的设置:

void Main()
{
   using(new SetupTransaction())
   {
    //Your test
   }
}

public class SetupTransaction : IDisposable
{
    private TransactionScope transaction;

    public SetupTransaction()
    {
        transaction = new TransactionScope();
        //Do your stuff here
    }

    public void Dispose()
    {
        transaction.Dispose();
    }
}

至于你得到的错误,你能准确发布你的实施方式吗?

答案 2 :(得分:0)

我成功使用的一种方法是创建一个实现设置和拆卸的基类。在setup方法中,您将创建一个新的事务范围并将其存储在私有类变量中。在拆解方法中,您回滚事务范围。

我正在使用NUNit,但MSTest的原则应该相同。这里的线索是SetUpTearDown每个单元测试之前和之后执行一次,以确保单元测试之间的隔离。

此外,正如@blech所提到的,必须运行Microsoft分布式事务处理协调器(MSDTC)服务才能使此解决方案正常工作。