TransactionScope causes TransactionManagerCommunicationException when used for DB unit tests - c#

I'd like to run some tests of stored procedures in my database without actually affecting the data (or, to put it more exactly, without a lasting impact after the test has run).
After some research I came up with the approach of using TransactionScope within my Visual Studio 2010 test project such as
using( new TransactionScope())
{
using( SqlConnection connection = new SqlConnection("someConnectionString"))
{
connection.Open();
using( SqlCommand command = new SqlCommand( "some sql", connection ))
{
// Do some database stuff...
}
}
}
Now this works fine as long as I put all of this within a single test method, i.e. all my changes to the database are automatically rolled back when the using block for TransactionScope is finished.
My problem is now that I'd like to do some database stuff in ClassInitialize so I only have to do it once per test class and not for every test method. When I create a public TransactionScope property and assign it an instance of TransactionScope in the ClassInitialize method, this works okay. As soon as I do any database related stuff in one of my test methods, I run into a TransactionManagerCommunicationException within that method.
I don't quite understand why that is the case, and I'd also like to know whether there is a mistake in my approach or how I can get it to work without having to set up the TransactionScope including all set up stuff for the tests within each test method again.
EDIT
Code excerpt 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, 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
}
}
The exception is
TransactionManagerCommunicationException was unhandled by user code
Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool.
I understand that I could try and change the settings, but I do not understand why I get the exception to begin with - what is different compared to having the code above in a single (test) method?
Thanks in advance and
best regards
G.

Your exception is saying that MSDTC isn't enabled. My guess is that when you were using TransactionScope individually, it was just creating local SQL transactions -- which don't require DTC. However, when you share a TransactionScope over multiple connections, the transaction gets "promoted" to a distributed transaction through the DTC, which you may not have enabled.
Try enabling network access on MSDTC on your local machine and the server. The steps for doing so vary a little depending on your OS. Here's how to do it in Win 2003 Server. Here's a link for Win 2008. Note that you will likely need to enable DTC through your firewalls as well (explained in the last link...)

You could create your setup something 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();
}
}
As for the error you're getting, could you post exactly how you're using your implementation?

One approach I have used with success is creating a base class that implements setup and teardown. Within the setup method you create a new transaction scope and store it in a private class variable. Within the teardown method you rollback the transaction scope.
I'm using NUNit, but the principle should be the same for MSTest. The clue here is that SetUp and TearDown executes once before and after each unit test to ensure isolation among unit tests.
Also, as #blech mentions, the Microsoft Distributed Transaction Coordinator (MSDTC) service must be running for this solution to work.

Related

Writing data to two databases with EF within one Transaction Scope

We are using Entity Framework 6 and are dealing with the following requirement:
We are writing data to two different and independent databases let's call them Main and External, they both exist on the same instance of SQL Server and the connection string used to connect to them are identical except for the initial catalog value.
In the application each DB has its own EF DBContext.
The Main DB has an AuditLog table in which data changes get recorded.
Data changes that happen on the External DB need to be recorded on the AuditLog table within Main DB.
Because of certain restrictions that are not important to discuss the way we implemented this was to have ExternalContext to have a reference of MainContext in order to call MainContext.SaveAuditLogs whenever ExternalContext.SubmitChanges was called, like so (only showing relevant code):
public class ExternalContext : IDataContext
{
private readonly IAuditLogContext auditLogContext;
public ExternalContext (IAuditLogContext auditLogContext){
this.auditLogContext = auditLogContext;
}
public override void SaveChanges()
{
base.SaveChanges();
this.auditLogContext.SaveAuditLogs(auditLogs);
}
}
public class MainContext : IAuditLogContext
{
public void SaveAuditLogs(List<AuditLog> auditLogs)
{
this.Set<AuditLog>().AddRange(auditLogs);
this.SaveChanges();
}
}
An example of how this is being used:
public class SomeBusinessClass
{
private readonly IDataContext dataContext;
public SomeBusinessClass(IDataContext dataContext)
{
this.dataContext = dataContext;
}
public void SomeOperation(Entity someEntity)
{
.....
using(var scope = new TransactionScope())
{
this.dataContext.Insert(someEntity);
this.dataContext.SaveChanges();
}
scope.Complete();
}
}
For this to work the Distributed Transaction Coordinator service needs to be running. When tested on development environments it works fine but on QA environments it fails with the error message as if the Distributed Transaction Coordinator is not running even though it is.
Apparently this happens because in our development environments the DB Server and the Web Server are the same computer weather in QA they are two separate boxes and the DTC does not like it when there are multiple servers and the two operations run within a TransactionScope, if we remove the TransactionScope then it works fine on both environments but then there is the risk that if the AuditLog fails the whole transaction is not rolled back.
How can we make this work?
Thank you.

C# Rollback Outer TransactionScope Regardless of What Happens In Nested TransactionScopes

First of all, I have read this similar question: Nested/Child TransactionScope Rollback but the answer didn't provide a solution and our questions are slightly different.
Basically, I have a database integration test which uses a transaction scope (In actuality the scope is managed in setup/teardown in an abstract class)
[Test]
public void MyTest()
{
using(var outerScope = new TransactionScope())
{
Assert.True(_myService.MyMethod());
var values = _myService.AnotherMethod();
}
}
And MyService.MyMethod also uses a TransactionScope
public bool MyMethod()
{
using(var innerScope = new TransactionScope())
using(var con = conFact.GetOpenConnection())
{
var cmd = con.CreateCommand();
//set up and execute command
if (isCheck) scope.Complete();
return isCheck;
}
}
So in theory, MyMethod only commits its changes if isCheck is true, but regardless of whether that transaction commits or not, when the method is tested, it will be rolled back.
It works as expected unless isCheck is false, in which case I get the following exception: System.Transactions.TransactionException : The operation is not valid for the state of the transaction.
I think what happened here was that since innerScope used TransactionScopeOption.Required, it joined the transaction used in outerScope. Once innerScope gets disposed when isCheck is false, outerScope is also disposed (This is what I don't want to happen!) so when I try to get another connection after MyMethod has been called, the outerScope is already disposed.
Alternatively, if I specify TransactionOption.RequiresNew, I get this exception: System.Data.SqlClient.SqlException : Timeout expired.
I have tried using a SqlTransaction with a specified savepoint, and different combinations of TransactionOption to no avail.
There is no such thing as nested transactions. You can nest scopes but all that the nested scopes do is attach the the already running transaction. You cannot treat an inner scope independently from the other scope (except of course with RequiresNew which simply creates an independent transaction).
The functionality that you want does not exist in System.Transactions.
Savepoints are the only way to create something that looks like nested transactions. But then again SQL Server is prone to kill your entire transaction for arbitrary reasons. It is unpredictable what errors roll back the statement and what errors roll back the transaction. (Yes, this makes no sense.)

Sharing a connection and a transaction in EF across multiple contexts (UnintentionalCodeFirstException)

The previous version and question are provided as an added context below. The improved problem formulation and question could be as follows:
How does one share a transaction between multiple contexts in EF 6.1.0 database first and .NET 4.5.2 without doing a distributed transaction?
For that it looks like I need to share a connection between the multiple contexts, but the code examples and tutorials I've been looking at thus far haven't been that fruitful. The problem looks like is hovering around on how to define a functioning combination of a connection object and transaction object types so that EF database first object metadata is also built and found when constructing the object contexts.
That is, I would like to do akin to what has been described in the EF 6.n tutorials here. Some example code could be
int count1;
int count2;
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
//How to define this connection so as not to run into UnintentionalCodeFirstException?
//Creating a dummy context to obtain the connectiong string like so
//dummyContext.Database.Connection.ConnectionString and then using the connection will be greeted with the aforementioned exception.
using(var conn = new SqlConnection("..."))
{
using(var c1 = new SomeEntities(conn, contextOwnsConnection: false))
{
//Use some stored procedures etc.
count1 = await c1.SomeEntity1.CountAsync();
}
using(var c2 = new SomeEntities(conn, contextOwnsConnection: false))
{
//Use some stored procedures etc.
count2 = await c2.SomeEntity21.CountAsync();
}
}
}
int count = count1 + count2;
In the examples there are also other methods as to how to create a shared connection and a transaction, but as written, the culprit seem to be that if, say, I provide the connectiong string in (the "..." part) the previous snippet as dummyContext.Database.Connection.ConnectionString I'll get just an exception.
I'm not sure if I'm just reading the wrong sources or if there's something else that's wrong in my code when I try to share a transaction across multiple EF contexts. How could it be done?
I've read quite a few other SO posts regarding this (e.g. this) and some tutorials. They did not help.
I have a strange problem in that it looks I don't have the constructor overloads defined as in other tutorials and posts. That is, taking the linked tutorial link, I can't write new BloggingContext(conn, contextOwnsConnection: false)) and use a shared connection and an external transaction.
Then if I write
public partial class SomeEntities: DbContext
{
public SomeEntities(DbConnection existingConnection, bool contextOwnsConnection): base(existingConnection, contextOwnsConnection) { }
}
and use it like in the tutorials, I get an exception from the following line from the following T4 template generated code
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
I'm using .NET 4.5.2 and EF 6.1.0. I'ved constructed the edmx from an existing database and generated the code from there. In this particular situation I'm using Task Parallel threads to load dozens of SQL Server Master Data Services staging tables (yes, a big model) and to call the associated procedures (provided by the MDS one per table). MDS has its own compensation logic in case staging to some of the tables fails, but rolling back a transaction should be doable too. It just looks like I have a (strange) problem with my EF.
<Addendum: Steve suggested using straight TransactionScope. Without a shared connection that would require a distributed transaction, which isn't an option I can choose. Then if I try to provide a shared connection for the contexts (some options shown in the tutorials, one here I have the problem of "missing constructors". When I define one, I get the exception I refer in the code. All in all, this feels quite strange. Maybe there's something wrong in how I go about generating the DbContext and related classes.
<Note 1: It looks like the root cause is as in this blog post by Arthur (of EF developer team) Don't use Code First by mistake. That is, in database first development the framework seeks for the class-relational mappings as defined in the connection string. Something fishy in my connection string that is..?
Have you tried wrapping the calls in a transaction scope?
using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadCommitted }))
{
// Do context work here
context1.Derp();
context2.Derp();
// complete the transaction
scope.Complete();
}
Because of connection pool, using multiple EF DbContext which are using exactly the same connection string, under same transaction scope will usually not result in DTC escalation (unless you disabled pooling in connection string), because the same connection will be reused for them both (from the pool). Anyway, you can reuse the same connection in your case like this (I assume you already added constructor which accepts DbConnection and flag indicating if context owns connection):
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) {
// important - use EF connection string here,
// one that starts with "metadata=res://*/..."
var efConnectionString = ConfigurationManager.ConnectionStrings["SomeEntities"].ConnectionString;
// note EntityConnection, not SqlConnection
using (var conn = new EntityConnection(efConnectionString)) {
// important to prevent escalation
await conn.OpenAsync();
using (var c1 = new SomeEntities(conn, contextOwnsConnection: false)) {
//Use some stored procedures etc.
count1 = await c1.SomeEntity1.CountAsync();
}
using (var c2 = new SomeEntities(conn, contextOwnsConnection: false)) {
//Use some stored procedures etc.
count2 = await c2.SomeEntity21.CountAsync();
}
}
scope.Complete();
}
This works and does not throw UnintentionalCodeFirstExce‌​ption because you pass EntityConnection. This connection has information about EDMX metadata and that is what database first needs. When you pass plain SqlConnection - EF has no idea where to look for metadata and actually doesn't even know it should look for it - so it immediately assumes you are doing code-first.
Note that I pass EF connection string in code above. If you have some plain SqlConnection which you obtained by some other means, outside EF, this will not work, because connection string is needed. But, it's still possible because EntityConnection has constructor which accepts plain DbConnection. However, then you should pass reference to metadata yourself. If you are interested in this - I can provide code example of how to do that.
To check that you indeed prevent escalation in all cases - disable pooling (Pooling=false in connection string) and stop DTC service, then run this code - it should run fine. Then run another code which does not share the same connection and you should observe error indicating escalation was about to happen but service is not available.

Nested Transaction Scope for transactional Operation

I've got a doubt about transactionscope because I'd like to make a transactional operation where first I perform some CRUD operations (a transaction which inserts and updates some rows on the DataBase) and I got a result from the whole transaction (an XML).
After I got the XML I send the XML to a Web Service which my customer exposes to integrate my system with.
The point is, let's imagine that one day the WS that my customer exposes falls down due to a weekly or monthly support task that its IT Area perform, so everymoment I perform the whole thing It performs the DB operation but of course It will throw an exception at the moment that I try to call the WS.
After Searching on the Internet I started to think of Transaction Scope. My Data Access Method which is on my Data Access Layer already has a TransactionScope where I perform insert, update, delete, etc.
The following Code is what I'd like to try:
public void ProcessSomething()
{
using (TransactionScope mainScope = new TransactionScope())
{
FooDAL dl = new FooDAL();
string message = dl.ProcessTransaction();
WSClientFoo client = new WSClientFoo();
client.SendTransactionMessage(message);
mainScope.Complete();
}
}
public class FooDAL
{
public string ProcessTransaction()
{
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions(){ IsolationLevel = IsolationLevel.ReadCommitted}))
{
///Do Insert, Update, Delete and According to the Operation Generates a message
scope.Complete();
}
return transactionMessage;
}
}
The question is, is it correct to use TransactionScope to handle what I want to do ?
Thanks a lot for your time :)
TransactionScopeOption.Required in your FooDAL.ProcessTransaction method means in fact: if there is a transaction available, reuse it in this scope; otherwise, create a new one.
So in short: yes, this is the correct way of doing this.
But be advised that if you don't call scope.Complete() in FooDAL.ProcessTransaction, a call to mainScope.Complete() will crash with a 'TransactionAbortedException' or something like that, which makes sense: if a nested scope decides that the transaction cannot be committed the outer scope should not be able to commit it.

How can I use Sql CE 4 databases for functional tests

Due to the potential differences between Linq-to-Entities (EF4) and Linq-to-Objects, I need to use an actual database to make sure my query classes retrieve data from EF correctly. Sql CE 4 seems to be the perfect tool for this however I have run into a few hiccups. These tests are using MsTest.
The problem I have is if the database doesn't get recreated (due to model changes), data keeps getting added to the database after each test with nothing getting rid of the data. This can potentially cause conflicts in tests, with more data being returned by queries than intended.
My first idea was to initialize a TransactionScope in the TestInitialize method, and dispose the transaction in TestCleanup. Unfortunately, Sql CE4 does not support transactions.
My next idea was to delete the database in TestCleanup via a File.Delete() call. Unfortunately, this seems to not work after the first test is run, as the first test's TestCleanup seems to delete the database, but every test after the first does not seem to re-create the database, and thus it gives an error that the database file is not found.
I attempted to change TestInitialize and TestCleanup tags to ClassInitialize and ClassCleanup for my testing class, but that errored with a NullReferenceException due to the test running prior to ClassInitialize (or so it appears. ClassInitialize is in the base class so maybe that's causing it).
I have run out of ways to effectively use Sql CE4 for testing. Does anyone have any better ideas?
Edit: I ended up figuring out a solution. In my EF unit test base class I initiate a new instance of my data context and then call context.Database.Delete() and context.Database.Create(). The unit tests run a tad slower, but now I can unit test effectively using a real database
Final Edit: After some emails back and forth with Microsoft, it turns out that TransactionScopes are now allowed in SqlCE with the latest release of SqlCE. However, if you are using EF4 there are some limitations in that you must explicitly open the database connection prior to starting the transaction. The following code shows a sample on how to successfully use Sql CE for unit/functional testing:
[TestMethod]
public void My_SqlCeScenario ()
{
using (var context = new MySQLCeModelContext()) //ß derived from DbContext
{
ObjectContext objctx = ((IObjectContextAdapter)context).ObjectContext;
objctx.Connection.Open(); //ß Open your connection explicitly
using (TransactionScope tx = new TransactionScope())
{
var product = new Product() { Name = "Vegemite" };
context.Products.Add(product);
context.SaveChanges();
}
objctx.Connection.Close(); //ß close it when done!
}
}
In your TestInitialize you should do the following:
System.Data.Entity.Database.DbDatabase.SetInitializer<YourEntityFrameworkClass>(
new System.Data.Entity.Database.DropCreateDatabaseAlways<YourEntityFrameworkClass>());
This will cause entity framework to always recreate the database whenever the test is run.
Incidentally you can create an alternative class that inherits from DropCreateDatabaseAlways. This will allow you to seed your database with set data each time.
public class DataContextInitializer : DropCreateDatabaseAlways<YourEntityFrameworkClass> {
protected override void Seed(DataContext context) {
context.Users.Add(new User() { Name = "Test User 1", Email = "test#test.com" });
context.SaveChanges();
}
}
Then in your Initialize you would change the call to:
System.Data.Entity.Database.DbDatabase.SetInitializer<YourEntityFrameworkClass>(
new DataContextInitializer());
I found the approach in the "final edit" works for me as well. However, it's REALLY annoying. It's not just for testing, but any time you want to use TransactionScope with Entity Framework and SQL CE. I want to code once and have my app support both SQL Server and SQL CE, but anywhere I use transactions I have to do this. Surely the Entity Framework team should have handled this for us!
In the meantime, I took it one step farther to make it a little cleaner in my code. Add this block to your data context (whatever class you derive from DbContext):
public MyDataContext()
{
this.Connection.Open();
}
protected override void Dispose(bool disposing)
{
if (this.Connection.State == ConnectionState.Open)
this.Connection.Close();
base.Dispose(disposing);
}
private DbConnection Connection
{
get
{
var objectContextAdapter = (IObjectContextAdapter) this;
return objectContextAdapter.ObjectContext.Connection;
}
}
This makes it a lot cleaner when you actually use it:
using (var db = new MyDataContext())
{
using (var ts = new TransactionScope())
{
// whatever you need to do
db.SaveChanges();
ts.Complete();
}
}
Although I suppose that if you design your app such that all changes are committed in a single call to SaveChanges(), then the implicit transaction would be good enough. For the testing scenario, we want to roll everything back instead of calling ts.Complete(), so it's certainly required there. I'm sure there are other scenarios where we need the transaction scope available. It's a shame it isn't supported directly by EF/SQLCE.

Categories

Resources