TransactionScope raises a TransactionManagerCommunicationException when used for unit tests of a database

I would like to run some tests of stored procedures in my database, without actually affecting the data (or, more precisely, without prolonged exposure after running the test).

After some research, I came up with an approach to using TransactionScope in my Visual Studio 2010 test project, for example

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

Now it works great, as long as I put all this into one test method, i.e. all my changes to the database are automatically rolled back when using the TransactionScope block.

My problem is that I would like to do some database stuff in ClassInitialize, so I need to do this only once for each test class, and not for each test method. When I create a public TransactionScope property and assign it a TransactionScope instance in the ClassInitialize method, this works fine. As soon as I make any database related material in one of my test methods, I throw a TransactionManagerCommunicationException inside this method.

I don’t quite understand why this is so, and I would also like to know if there is an error in my approach or how I can make it work without the need to configure TransactionScope, including all the configured material for tests within each test method again.

EDIT

An excerpt from the code below, I hope this gives enough information:

 public TransactionScope Scope { get; set; } [ClassInitialize] public static void ClassInitialize( TestContext testContext ) { Scope = new TransactionScope(); // Do some db set up stuff, eg 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 } } 

The exception is


TransactionManagerCommunicationException was not handled by user code Network access for Distributed Transaction Manager (MSDTC) has been disabled. Enable DTC to access the network in the security configuration for MSDTC using the component services administration tool.


I understand that I can try and change the settings, but I don’t understand why I am throwing an exception - how is it different from the code above in one (test) method?

Thanks in advance and

Best wishes

G.

+4
source share
3 answers

Your exception states that MSDTC is not enabled. I assume that when you used TransactionScope separately, it simply created local SQL transactions that did not require DTC. However, when you share a TransactionScope across multiple connections, the transaction gets "pushed" to the distributed transaction via DTC, which you may not have activated.

Try enabling network access in MSDTC on your local computer and on the server. The steps for this vary slightly depending on your OS. Here's how to do it in Win 2003 Server . Here is the link for Win 2008 . Please note that you will probably also need to enable DTC through your firewalls (explained in the last link ...)

+4
source

You can create your customization like this:

 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(); } } 

Regarding the error you are getting, can you specify exactly how you are using your implementation?

0
source

One approach that I have used with success is to create a base class that implements configuration and disabling. As part of the configuration method, you create a new transaction scope and save it in a private class variable. As part of the teardown method, you rollback the transaction scope.

I use NUNit, but the principle should be the same for MSTest. The key here is that SetUp and TearDown run once before and after each unit test to ensure isolation between unit tests.

In addition, as @blech mentions, the Microsoft Distributed Transaction Transaction Coordinator (MSDTC) service must be running for this solution to work.

0
source

Source: https://habr.com/ru/post/1336126/


All Articles