Implementing Dispose
I noted earlier that the DistributedTransaction object implements the IDisposable pattern. In transactional applications, it's of paramount importance that you always either commit or rollback a transaction, so implementing IDisposable is required to close the transaction even if a developer forgets to explicitly insert the required code in a business layer method. While it's preferable for client code to explicitly commit or rollback the transaction, you can't guarantee that will occur, so it's best to write a robust framework that guarantees fallback transaction management. The DistributedTransaction object can't call the
Dispose method, so the client developer should always call it. The code for the
Dispose method looks like this:
Protected Sub Dispose(ByVal disposing As Boolean)
If _disposed Then Exit Sub
If disposing Then
' -- Avoid runtime error when disposing
' (connction broken, etc.)
Try
' -- if transaction has not been committed or rolled
' back we need to close it
If Not _done Then
' -- Commits if the object is happy,
' otherwise rolls back
If Me._happy Then
_sqlTransaction.Commit()
Else
_sqlTransaction.Rollback()
End If
End If
Catch ex As Exception
End Try
Try
' -- Important: closes and releases reference
' to transaction
_connection.Close()
_connection.Dispose()
_sqlTransaction.Dispose()
Catch ex As Exception
Debug.WriteLine(
"Error closing transaction's resources: "
& ex.Message)
End Try
End If
_disposed = True
End Sub
In C#:
protected void Dispose(bool disposing){
if (_disposed) {return;}
if (disposing) {
// Avoid runtime error when disposing
// (connection broken, etc.)
try {
// if transaction has not been committed or
// rolled back, we need to close it
// Commits if the object is happy, otherwise
// rolls back
if (!_done) {
if (this._happy) {
_sqlTransaction.Commit();
}
else {
_sqlTransaction.Rollback();
}
}
}
catch (Exception ex) {}
try{
// -- Important: closes and releases reference to
// transaction
_connection.Close();
connection.Dispose();
_sqlTransaction.Dispose();
}
catch (Exception ex) {
Debug.WriteLine("Error closing transaction's " +
resources: " + ex.Message);
}
}
_disposed = true;
}
Clearly, you may implement the standard IDisposable pattern, so you may define these other standard functions:
Public Sub Dispose() Implements System.IDisposable.Dispose
' -- makes resources cleanup
Dispose(True)
' -- suppress call to finalize
GC.SuppressFinalize(Me)
End Sub
Protected Overrides Sub Finalize()
' -- release all unmanaged resources
Dispose(False)
End Sub
In C#:
public void System.IDisposable.Dispose() {
// makes resources cleanup
Dispose(true);
// suppress call to finalize
GC.SuppressFinalize(this);
}
protected void Finalize {
' -- release all unmanaged resources
Dispose(false);
}
Creating a distributed transaction schema applied to the application layer requires considerably more code and work in .NET than in COM+. Unfortunately, using Enterprise Services carries baggage that you may want to avoid, unless you need it for more than just transactions. The framework described in this article, built around the ADO.NET Transaction object, lets you write business and data access layer methods in a manner similar to COM+, using little extra code and with a similarly efficient structure. Of course, COM+ and its transactions can provide more functionality than is captured in this framework, but for basic transactions, this framework should provide all you need.