The PostSharp.Samples.Transaction
project demonstrates
the use of PostSharp Laos to author a custom attribute that defines
transaction boundaries. This sample relies on the System.Transactions
namespace. It demonstrates the use of the custom attribute
with the classic "bank account transfer" scenario.
This sample is written in Visual Basic .NET.
TransactionAttribute
class of System.EnterpriseServices
, but relying on the lighter System.Transactions
framework. Transaction boundaries can be defined procedurally using the TransactionScope
class, so our only ambition is to author a TransactionScopeAttribute
class that would use TransactionScope
as a back-end.What we need to do is to:
TransactionScope
class in the beginning of the method, TransactionScope
in case of success (i.e. in case of absence of exception).TransactionScope
instance in the finally clause.This is exactly the cup of tea of the OnMethodBoundaryAspect
class. We will basically override two methods: OnEntry
and OnExit
. In the first, we will create the TransactionScope
instance and, in the second, we will eventually commit it and anyway dispose it.
In order to expose the full functionality of the TransactionScope
class in our custom attribute, we add the properties ScopeOption
, IsolationLevel
and Timeout
, which will be the parameters of the TransactionScope
constructor.
The principal methods look like this:
Public Overrides Sub OnEntry(ByVal eventArgs As MethodExecutionEventArgs)
Dim transactionOptions As TransactionOptions
transactionOptions.Timeout = TimeSpan.FromSeconds(Me._timeout)
transactionOptions.IsolationLevel = Me._isolationLevel
eventArgs.MethodExecutionTag = New TransactionScope(Me._scopeOption, transactionOptions, Me._interopOption)
End Sub
Public Overrides Sub OnExit(ByVal eventArgs As MethodExecutionEventArgs)
Dim transactionScope As TransactionScope = eventArgs.MethodExecutionTag
If eventArgs.Exception Is Nothing Then
transactionScope.Complete()
End If
transactionScope.Dispose()
End Sub
In order to demonstrate how to use the TransactionScope
custom attribute, we have created a class Account
that supports in-memory transactions. This implementation is
uninteresting for the current discussion, so we don't comment it
further.
Now take the typical example of a bank transfer. Suppose that we have a Transfer method that should adjust the balances of two accounts in a transaction. The code is now as simple as this:
<TransactionScope()> _
Sub Transfer(ByVal fromAccount As Account, ByVal toAccount As Account, ByVal amount As Decimal)
fromAccount.Balance -= amount
toAccount.Balance += amount
End Sub