Nested TransactionScope fails in tests - c#

I'm using MSTest to run some automated tests against a MySQL 5.5.19 DB via the MySQL Connector and using EntityFramework 4.3.
I'm attempting to use TransactionScope in my DB-accessing class library to perform a rollback when needed. Additionally, in my test code I wish to use TransactionScope to put the DB back into a known state before each test. I use the TestInitialize and TestCleanup methods to accomplish this. Those look like so:
[TestInitialize()]
public void MyTestInitialize()
{
testTransScope = new TransactionScope(TransactionScopeOption.RequiresNew);
}
[TestCleanup()]
public void MyTestCleanup()
{
Transaction.Current.Rollback();
testTransScope.Dispose();
}
Based on the construction of the TransactionScope object there in the initialize function, I believe I should be getting a new transaction scope (there isn't an "ambient" one existing so I believe this ".RequiresNew" isn't technically important here since ".Required" would produce the same result. Since I don't specify a timeout value, it provides me with the default timeout, which I understand to be 60 seconds. Plenty of time for my given test to run.
I've got a function called AddDessert(DessertBiz dessertBizObject) which looks, in part, something like this:
using (var transScope = new TransactionScope(TransactionScopeOption.Required))
{
try
{
// ...
context.Desserts.Add(dessert);
context.SaveChanges();
var dessertId = dessert.Id;
DoOtherDessertStuff(dessertId, dessertBizObject);
transScope.Complete();
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.ToString());
}
}
And this function is called by one of my tests.
Since I've specified TransactionScopeOption.Required here, I expect that it will use the "ambient" transaction scope created by the MyTestInitialize function.
My test is arranged to make this DoOtherDessertStuff function to fail and throw an exception, so the call to transScope.Complete(); doesn't happen and the rollback occurs automatically when exiting the using block in the AddDessert function.
The problem I have here is that since it uses the ambient transaction scope created in the MyTestInitialize function, my test Assert calls don't happen because the transaction scope rollback happened - at least this is what I think is happening. I verified that Transaction.Current.TransactionInformation.Statusis TransactionStatus.Aborted, so I feel pretty sure this is what's happening.
Great, so I thought I would change my AddDesert method to look exactly as above except that I would nest a transaction scope rather than using the ambient one, some my using line looks looks like this:
using (var transScope = new TransactionScope(TransactionScopeOption.RequiresNew))
The intent here was that I could nest these transaction scopes, let the rollback in my production code occur and then still check my Asserts in my test code.
But what I am finding is that I get the following error:
System.IO.IOException: Unable to read data from the transport connection: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host failed to respond.
Ideas?

Very good question. When you reference datacontext inside your testmethod after rollback, it will not be available. You need to suppress that. You don't need to specify required option. It is the default option.
Test Method:
[TestMethod()]
public void CreateTestCheckContextCorrectly()
{
MailJobController target = new MailJobController();
target.AddDessert("dessert for Omer");
//With suppress, even if you rollback ambient trans, suppress will ignore ambient trans. You need to reference new context, previous context from controller may be disposed.
using (var suppressscope = new TransactionScope(TransactionScopeOption.Suppress))
{
var newdbcontextref = new DbEntities();
int recordcount = newdbcontextref.StatusDefinitions.Where(x => x.Name == "dessert for Omer").Count();
Assert.AreEqual(0, recordcount);
}
}
Controller method:
public void AddDessert(string dessert)
{
using (var transScope = new TransactionScope())
{
try
{
// ...
StatusDefinition statusDefinition = new StatusDefinition() {Name = dessert};
db.StatusDefinitions.AddObject(statusDefinition);
db.SaveChanges();
Console.WriteLine("object id:"+statusDefinition.StatusDefinitionId);
throw new Exception("hee hee");
transScope.Complete();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}

Related

Dapper + Oracle + TransactionScope = Transaction has aborted

I've looked around but couldn't find an appropriate (or one that is satisfactory to me) on how to address an issue we are having.
I use Dapper and ODP.NET 12 Managed drivers. The problem is not encountered when TransactionScope is not used.
When performing commands under a transaction scope, I get an error "Transaction has aborted" via the TransactionAbortedException thrown.
Observed behaviors:
1) TransactionAbortedException is thrown if and only if the transaction is completed and the TransactionScope is disposed. The point at when the exception is thrown is during dispose.
2) Despite the exception, the transaction concept actually works! After Complete() is invoked, the changes are committed into the database.
Below is the code snippet.
// Conn string: "Data Source=OraDB;Persist Security Info=True;User ID=userxxx;Password=passwordxxx;" providerName="Oracle.ManagedDataAccess.Client
// Note: GetDbFactory().Create() returns a DbConnection object
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted }))
using (var dbConn = GetDbFactory().Create())
{
foreach (MyDTO dto in dtoList)
{
var tableDAO= new TableDAO(dbConn);
MyEntity entity = new MyEntity()
{
Field1 = dto.Field1,
Field2 = dto.Field2
};
tableDAO.AddOrUpdate(entity);
}
// Commit changes
scope.Complete();
}
// This method is under the DAO class
public void AddOrUpdate(MyEntity entity)
{
// Verify arguments
entity.AsArgumentThrowExceptionIfNull("entity");
// build param
OracleDynamicParameters parameters = new OracleDynamicParameters();
parameters.Add("P_FIELD1", entity.Field1);
parameters.Add("P_FIELD2", entity.Field2);
// execute SP
dbConnection.Execute("PRC_MY_ENTITY_ADDORUPDATE", parameters, commandType: CommandType.StoredProcedure);
}//-- end AddOrUpdate()
==================================================================
UPDATE (09-Apr-15)
I have changed my approach and use the following pattern for now for Oracle. Our code deals with connections in both Oracle and SQL Server so I'd much prefer that the coding pattern is consistent, but until a solution is found with using Oracle+TransactionScope, we'll use the pattern below for Oracle command execution:
using (var dbConnection = dbConnFactory.Create())
{
// Open db connection
dbConnection.Open();
using (var trans = dbConnection.BeginTransaction())
{
bool isSuccess = false;
try
{
// Perform DB operations here
trans.Commit();
isSuccess = true;
}
finally
{
if(!isSuccess) trans.Rollback();
}
}
}
First, the exception is documented as possible:
A call to the Dispose method marks the end of the transaction scope. Exceptions that occur after calling this method may not affect the transaction.
This is in the documentation on the TransactionScope class (https://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28v=vs.110%29.aspx).
One other thing I noticed is that the complete on the transaction is being before the connection is closed. I'd change this to close the connection and then complete the transaction.
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted }))
{
using (var dbConn = GetDbFactory().Create())
{
foreach (MyDTO dto in dtoList)
{
var tableDAO= new TableDAO(dbConn);
MyEntity entity = new MyEntity()
{
Field1 = dto.Field1,
Field2 = dto.Field2
};
tableDAO.AddOrUpdate(entity);
}
}
// Commit changes
scope.Complete();
}
I don't see anything wrong with that code. However, if that loop runs long enough, the transaction is going to timeout. You'll then get the exception in question the next time you do an operation against the database. I would try increasing the timeout - Timeout is a property on the TransactionScopeOption class.
I am resorting to using BeginTransaction() as the final approach (refer to my update in my original post). I have read more about why TransactionScope() was failing.
1) ODP.Net promotes to distributed transaction even when using a single DB connection when connecting to Oracle 10g and below (source). Lo and behold, the database I'm connecting to is indeed 10g.
2) You'll need Oracle MTS Service installed. This I didn't have setup on my dev machine.

Unit Of Work inside Transaction scope and Entity Framework

I get an exception when I try to save an entity inside a TransactionScope, the exception I get is:
The operation is not valid for the state of the transaction
However on another machine I get a different exception on the same code block:
The transaction operation cannot be performed because there are pending requests working on this transaction
The transaction implementation in my method is:
TransactionHelper.Transaction(() =>
{
if (sys.WfId.HasValue)
{
var sysData = GeneralMapping.GetSysData(model, ThisUser);
DoAction(sys, model, sysData);
if (!_sysClosed)
{
_Repository.InsertOrUpdate(sys);
_Uow.Save();
}
}
});
TransactionHelper:
public static class TransactionHelper
{
public static void HandleTransaction(Action action)
{
using (var tx = CreateScope())
{
action();
tx.Complete();
}
}
private static TransactionScope CreateScope(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
{
var tOptions = new TransactionOptions
{
IsolationLevel = isolationLevel,
Timeout = TransactionManager.MaximumTimeout
};
return new TransactionScope(TransactionScopeOption.Required, tOptions);
}
}
This was working perfectly well without any problem, however recently after I started to see these exceptions, I have been told that this implementation might lead into a deadlocks, as the unit of work is a transaction (Business Transaction - Martin Fowler) inside a transaction scope, is that possible to be the reason? and if so, what can be the alternative implementation if needed?
This exception occurred by several reasons like transaction timeout, nesting two transactions inside each other, two open connection inside of transaction scope, using single DbContext instance for entire web application and hence DbContext has it's own UOF, it may some conflict occurs between theme for some reason. You can use System.Transactions.Transaction.Current.TransactionInformation.Status for identifying status of transaction and it my aborted before exception thrown.
Change return new TransactionScope(TransactionScopeOption.Required, tOptions); to return new TransactionScope(TransactionScopeOption.RequiresNew, tOptions); and test it again and see what happen.

DbContextTransaction and Multi-thread: The connection is already in a transaction and cannot participate in another transaction

I got this error when I was trying to call same method with multiple threads: The connection is already in a transaction and cannot participate in another transaction. EntityClient does not support parallel transactions.
And I found that my issue somehow is similar to this: SqlException from Entity Framework - New transaction is not allowed because there are other threads running in the session
My scenario:
I have a class that is instantiated by multiple theads, each thread - new instance:
public MarketLogic()
{
var dbContext = new FinancialContext();
AccountBalanceRepository = new AccountBalanceRepository(dbContext);
CompositeTradeRepository = new CompositeTradeRepository(
new OrderRepository(dbContext)
, new PositionRepository(dbContext)
, new TradeRepository(dbContext));
CompositeRepository = new CompositeRepository(
new LookupValueRepository(dbContext)
, new SecurityRepository(dbContext)
, new TransactionRepository(dbContext)
, new FinancialMarketRepository(dbContext)
, new FinancialMarketSessionRepository(dbContext)
);
}
In MarketLogic class, SavePosition() is used to save information into database using Entity Framework DbContext. (SaveChanges()) method.
private void SavePosition()
{
using (DbContextTransaction transaction = CompositeTradeRepository.OrderRepository.DbContext.Database.BeginTransaction())
{
try
{
// business logic code, **this take some times to complete**.
position = EntityExistsSpecification.Not().IsSatisfiedBy(position)
? CompositeTradeRepository.PositionRepository.Add(position)
: CompositeTradeRepository.PositionRepository.Update(position);
transaction.Commit();
}
catch (Exception exception)
{
// some code
transaction.Rollback();
}
}
}
public Position Add(Position position)
{
// some code
// context is a instance of FinancialContext, this class is generated by Entity Framework 6
context.SaveChanges();
}
In my scenario, the issue happened when there are 2 threads and more try to call new MarketLogic().SavePosition().
I can see that while the first transaction is not completed yet, the second thread come in and start a new transaction.
But I dont understand why 2 threads are in different DbContext object BUT the error still happens
So what is wrong? Or did I miss something?
My fault, I left the repositories as static, so all thread shared same repositories, which means they shared same DbContext, which caused the issue when the EF didn't finished permitting changes yet and other call to SaveChanges() is made. So EF throwed exception.

How to rollback in EF4 for Unit Test TearDown?

In my research about rolling back transactions in EF4, it seems everybody refers to this blog post or offers a similar explanation. In my scenario, I'm wanting to do this in a unit testing scenario where I want to rollback practically everything I do within my unit testing context to keep from updating the data in the database (yeah, we'll increment counters but that's okay). In order to do this, is it best to follow the following plan? Am I missing some concept or anything else major with this (aside from my SetupMyTest and PerformMyTest functions won't really exist that way)?
[TestMethod]
public void Foo
{
using (var ts = new TransactionScope())
{
// Arrange
SetupMyTest(context);
// Act
PerformMyTest(context);
var numberOfChanges = context.SaveChanges(SaveOptions.AcceptAllChangesAfterSave);
// if there's an issue, chances are that an exception has been thrown by now.
// Assert
Assert.IsTrue(numberOfChanges > 0, "Failed to _____");
// transaction will rollback because we do not ever call Complete on it
}
}
We use TransactionScope for this.
private TransactionScope transScope;
#region Additional test attributes
//
// Use TestInitialize to run code before running each test
[TestInitialize()]
public void MyTestInitialize()
{
transScope = new TransactionScope();
}
// Use TestCleanup to run code after each test has run
[TestCleanup()]
public void MyTestCleanup()
{
transScope.Dispose();
}
This will rollback any changes made in any of the tests.

Rolling back records created by PersistenceSpecifications in Fluent NHibernate

I'm learning some Fluent NHibernate and I've run across the semi-awesome PersistenceSpecification class.
I've set it up in a unit test to verify my mappings and it works great. However, it leaves the record in the database when done. I tried throwing it in a transaction so I can rollback the changes but I get an error:
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'AdoTransaction'..
Without a transaction I have to figure out the ID's of the record, retrieve them and delete them and that doesn't seem very elegant.
Any thoughts?
EDIT:
Here is the code snippet:
var factory = GetSessionFactory();
using (var session = factory.OpenSession())
using (var transaction = session.BeginTransaction())
{
new PersistenceSpecification<TimePeriod>(session)
.CheckProperty(x => x.EndDate, DateTime.Today)
.VerifyTheMappings();
transaction.Rollback();
}
Try setting the IsolationLevel on the transaction. This snippet worked for me:
using (var trans = _session.BeginTransaction(IsolationLevel.ReadUncommitted))
{
new PersistenceSpecification<Event>(_session)
.CheckProperty(p => p.StartTime, new DateTime(2010, 1, 1))
.VerifyTheMappings();
trans.Rollback();
}
The PersistenceSpecification is usually used with an in-memory database like SQLite, that's why it doesn't roll anything back. I believe there's a constructor overload that takes an ISession instance, have you tried getting a transaction from there then rolling that back after?
I think the issue here is VerifyTheMappings() calls TransactionSave() which does a tx.Commit() to the database. As James indicated, this technique seems to work great for throw away in-memory testing techniques. This would not work in the case of testing mappings against a legacy database.
Setting IsolationLevel.ReadUncommitted will work, but only incidentally, since all it is doing is telling the session that it can read without needing a new transaction (a dirty read, in DBMS parlance) - so Session.Transaction.Commit () doesn't have to commit a database transaction before the verification reads. This also means that it is not necessarily testing what you think it is testing! (I also think this has probably-questionable support amongst non-MS SQL databases). The answer from leebrandt works because of the explicit rollback, not the isolation level (nb. At the time of the answer this helped more than it does now, see note below).
The good news is that the correct way to do this is to just rollback the transaction manually. Session.Transaction is automatically replaced whenever the transaction is committed, so you'll need to hold a reference to it, and you'll have to open one explicitly anyways, since TransactionalSave () checks if the current transaction is active and creates (and disposes!) its own if not. I typically test all of my mappings in the same fixture, where I also verify the factory creation and a few other infrastructural persistence things, so I like the following pattern for this to keep the plumbing down:
class TestFixture {
static ISessionFactory factory = CreateMyFactorySomehowHere();
ISession session;
ITransaction tx;
public void Setup ()
{
session = factory.OpenSession ();
tx = session.BeginTransaction ();
}
public void Cleanup ()
{
tx.Rollback ();
tx.Dispose ();
session.Close ();
}
public void TestAMappingForSomething ()
{
var spec = new PersistenceSpecification<Something> (session);
spec.VerifyTheMappings ();
}
}
Obviously, insert your own test-framework-specific terminology and attributes/annotations wherever, but you get the idea.
I've just now noticed how old this question is: this behavior was fixed in this commit in July 09, to handle existing transactions nicely so that the above works! Clearly this is what you were doing originally anyways.
i think that it's very important to do this testing with your real db, to see that his tables definition r ok, so i'v developed a very simple class that perform a crud test on a mapped entity and roll back at the end;
internal class GenericMappingTesterWithRealDB<T> where T : IIdentifiable
{
public T EntityToTest { get; set; }
public Func<T, object> PerformEntityManipulationBeforeUpdate { get; set; }
public GenericMappingTesterWithRealDB()
{
Assume.That(SessionFactoryProvider.NewSession,Is.Not.Null);
}
public void RunTest()
{
using (ISession session = SessionFactoryProvider.NewSession)
using (ITransaction transaction = session.BeginTransaction())
{
try
{
session.Save(EntityToTest);
var item = session.Get<T>(EntityToTest.ID);
Assert.IsNotNull(item);
if (PerformEntityManipulationBeforeUpdate != null)
{
PerformEntityManipulationBeforeUpdate.Invoke(EntityToTest);
}
session.Update(EntityToTest);
session.Delete(EntityToTest);
session.Save(EntityToTest);
}
catch (Exception e)
{
Assert.Fail(e.Message, e.StackTrace);
}
finally
{
transaction.Rollback();
}
}
}
}
IIdentifiable in my project is the most basic interface of my entities
the class is using the nunit.framework but u can do it with every testing framework u want
sessionfactoryprovider needs to supply the isession obj
here is a sample of use
/// <summary>
/// Testing the mapping of our entities.
/// there must be a server connection for this kind of test.
/// </summary>
[TestFixture]
internal class someMappingTest
{
[Test(Description = "Check the Encoding Profile FluentNHibernate Mapping")]
[Timeout(20000)]
public void checkthatMappingWorks()
{
// creatw the new entity
TestedType testOn = new TestedType();
// set the initialization values
testOn.Name = "TestProfileExecution";
// create the test object
new GenericMappingTesterWithRealDB<TestedType>
{
// assign an entity
EntityToTest = testOn,
// assign new values for update check
PerformEntityManipulationBeforeUpdate =
delegate(TestedType testedTypeBeingTested)
{
return testedTypeBeingTested.Name = "Updateing Test";
}
}.
// call run test to perform the mapping test.
RunTest();
}
}

Categories