How does Dispose () know that it is called due to an exception?

I would like to write a simple unit of the working class that will behave as follows:

using (var unitOfWork = new UnitOfWork()) { // Call the data access module and don't worry about transactions. // Let the Unit of Work open a session, begin a transaction and then commit it. } 

This is what I have so far (any comments would be welcome if you think my design is wrong):

 class UnitOfWork : IDisposable { ISession _session; ITransation _transaction; . . . void Dispose() { _transaction.Commit(); _session.Dispose(); } } 

What I would like to do is roll back the transaction if the data acessus code made some kind of exception. Thus, the Dispose() method will look something like this:

  void Dispose() { if (Dispose was called because an exception was thrown) { _transaction.Commit(); } else { _transaction.RollBack(); } _session.Dispose(); } 

Does it make sense? And if so, how can this be done?

+7
source share
5 answers

In the end, I implemented a method with which all operations should be performed:

 class UnitOfWork : IDisposable { ... public void DoInTransaction(Action<ISession> method) { Open session, begin transaction, call method, and then commit. Roll back if there was an exception. } } 
+1
source

"Dispose ()" should have nothing to do with committing a transaction or rolling back. You must use the transaction in the Dispose () method. Changing the semantics of your Dispose () method will only lead to long-term confusion for you and everyone who uses your class.

The Transaction Commit () and RollBack () methods have nothing to do with the Dispose () method, since there is no correlation between the two methods and Dispose (), since you must manage the transaction regardless of the result.

This is the right template to use for connections and transactions. Note that Roolback (0 refers to the exception (and does not remove)

 connection.Open(); var trasnaction = null; try { transaction = connection.BeginTransaction(); ///Do Some work transaction.Commit(); } catch { transaction.Rollback(); } finally { if (transaction != null) transaction.Dispose(); connection.Close(); } 

Simulate this pattern in your UnitOfWork module using the Commit (), Roolback (), and Dispose () methods.

+6
source

You need UnitOfWork.Commit() at the end of the using block. Inside UnitOfWork , you have a committed flag that you check UnitOfWork.Dispose . If the false flag is, then you are UnitOfWork.Rollback() .

+2
source

The Dispose point is that it always starts. And, using this transaction commit icon, you do not need to know the difference.

 using (var unitOfWork = new UnitOfWork()) { // use unitOfWork here - No need to worry about transactions for this code. unitOfWork.Commit(); } 

Here we see that either an exception is thrown or unitOfWork is executed. Then we can have a bool in UnitOfWork to keep track of whether Commit was completed or not. Dispose can then not roll back. Thus, the unit of work is always either canceled or completed.

I would in any case avoid having Commit inside Dispose. To begin with, the ITransaction.Commit method ITransaction.Commit usually throw exceptions on errors - this is completely normal. However, the Dispose method should not throw exceptions. See this link and search Stackoverflow for more details on the reasons.

I think something like this with big bangs

 class UnitOfWork : IDisposable { ISession _session; ITransation _transaction; bool _commitTried; // stuff goes here void Commit() { _commitTried = true; _transaction.Commit(); } void Dispose() { if (!_commitTried) _transaction.Rollback(); _transaction.Dispose(); _session.Dispose(); } } 

The problem with forgetting to call Commit in general, I would say, is not so great, because if it is not perfect, the client code will not work, since the Rollback 'ed transaction and changes are not applied, which will be detected when the code is implemented inside the lamp or manually .

I really tried to handle this in one project using lambdas syntax like this

 _repository.InTransactionDo(ThisMethodIsRunInsideATransaction); 

Thus, customers did not have to worry about committing. I really ended up regretting it because he complicated the situation too much and wanted me to go with the above approach.

+2
source

A bit late in the game here, but check out this post from Ayende for a (slightly crazy) solution:

In the Dispose method, you just need to find out if it received "clean", that is, find out if an unhandled exception exists before the transaction:

 public class ExceptionDetector : IDisposable { public void Dispose() { if (Marshal.GetExceptionCode()==0) Console.WriteLine("Completed Successfully!"); else Console.WriteLine("Exception!"); } } 
+2
source

All Articles