Using a transaction scope
If a business transaction spans multiple handlers, there is always a risk of partial updates, since one handler might succeed in updating the data while others don't. To avoid this, it is possible to use a unit of work that wraps all handlers in a TransactionScope and ensures no partial updates. Use the following code to enable a wrapping scope:
var unitOfWork = endpointConfiguration.UnitOfWork();
unitOfWork.WrapHandlersInATransactionScope();
This requires the selected persistence to support enlisting in transaction scopes.
This might escalate to a distributed transaction if data across different databases is updated.
This API must not be used in combination with transports running in transaction scope mode. Wrapping handlers in a TransactionScope in such a situation throws an exception.
Controlling transaction scope options
The following options for transaction scopes used to wrap all handlers can be configured.
Isolation level
NServiceBus will by default use the ReadCommitted isolation level.
Change the isolation level using
var unitOfWork = endpointConfiguration.UnitOfWork();
unitOfWork.WrapHandlersInATransactionScope(
isolationLevel: IsolationLevel.RepeatableRead);
Transaction timeout
NServiceBus will use the default transaction timeout of the machine on which the endpoint runs.
Change the transaction timeout using
var unitOfWork = endpointConfiguration.UnitOfWork();
unitOfWork.WrapHandlersInATransactionScope(
timeout: TimeSpan.FromSeconds(30));
Or via .config file using a example DefaultSettingsSection.
Implementing custom unit of work
In scenarios, when custom unit of work is needed (e.g. to commit NHibernate transactions, or call SaveChanges on a RavenDB session without polluting handers logic) it can be implemented using a dedicated pipeline behavior.