With AutoComplete I mean being able to start a UoW scope which will automatically commit changes made if no uncatched exception is thrown nor a rollback operation is performed, inside it.
Adding such thing is pretty easy and that's why today I'm going to show you how to add a new "AutoComplete" option to UoWS so you can write code like this not caring about calling scope.Commit() at all:
using (new UnitOfWorkScope(UnitOfWorkScopeTransactionOptions.AutoComplete))
{
...
}
Let's start adding a new "AutoComplete" option to UnitOfWorkScopeTransactionOptions enumeration:
[Flags]
public enum UnitOfWorkScopeTransactionOptions
{
...
/// <summary>
/// Specifies that UoW scope will be commited automatically at Dispose(), if it has not been rolled back previously.
/// </summary>
AutoComplete = (1<<4)
}
Now we should modify UnitOfWorkScope class to handle this new option.
Step one, adding a new private field which will indicate whether this is an autocomplete enabled UoWS or not:
public class UnitOfWorkScope : IDisposable {
...
private bool _autocomplete = false;
...
}
Step two, modifying class' constructor:
public UnitOfWorkScope(IsolationLevel isolationLevel, UnitOfWorkScopeTransactionOptions transactionOptions)
{
_disposed = false;
if ((transactionOptions & UnitOfWorkScopeTransactionOptions.AutoComplete) != 0)
_autocomplete = true;
_currentTransaction = UnitOfWorkScopeTransaction.GetTransactionForScope(this, isolationLevel,
transactionOptions);
RegisterScope(this);
}
Step three, modifying Dispose() method to make it commit transaction if autocomplete is desired.
private void Dispose(bool disposing)
{
if (!disposing) return;
if (_disposed) return;
if (_currentTransaction != null)
{
if (_autocomplete == true)
_currentTransaction.Commit(this);
else
_currentTransaction.Rollback(this);
_currentTransaction = null;
}
UnRegisterScope(this);
_disposed = true;
}
Ok, now we need a unit test to ensure this is working as expected. So, let's add a new test at NCommon.Tests.UnitOfWorkScopeTests fixture.
[Test]
public void Disposing_AutoCommit_Scope_Does_Not_Calls_Rollback_On_Transaction()
{
var mockLocator = MockRepository.GenerateStub<IServiceLocator>();
var mockUOWFactory = MockRepository.GenerateMock<IUnitOfWorkFactory>();
var mockUOW = MockRepository.GenerateMock<IUnitOfWork>();
var mockTransaction = MockRepository.GenerateMock<ITransaction>();
mockLocator.Stub(x => x.GetInstance<IUnitOfWorkFactory>()).Return(mockUOWFactory);
mockUOWFactory.Expect(x => x.Create()).IgnoreArguments().Return(mockUOW);
mockUOW.Expect(x => x.BeginTransaction(IsolationLevel.ReadCommitted)).Return(mockTransaction);
mockUOW.Expect(x => x.Dispose());
mockTransaction.Expect(x => x.Commit());
mockTransaction.Expect(x => x.Dispose());
ServiceLocator.SetLocatorProvider(() => mockLocator);
using (new UnitOfWorkScope(UnitOfWorkScopeTransactionOptions.AutoComplete))
{
Assert.That(UnitOfWorkScope.HasStarted);
}
Assert.That(!UnitOfWorkScope.HasStarted);
mockUOWFactory.VerifyAllExpectations();
mockUOW.VerifyAllExpectations();
mockTransaction.VerifyAllExpectations();
}
Compile, tests, and.. that.. that.. that's all folks ;)
PD: Just one final note, this and a few more changes I've made to this library are available at my own subversion repository.
Right now I am making a few changes to this library, but once I feel my code is stable enough, and that I wont be making any major changes, I will try to submit all my additions to NCommon's maintainer proposing him merging my code back into his trunk.
Greets.

No comments:
Post a Comment