-
-
Notifications
You must be signed in to change notification settings - Fork 373
Unit of work
If you intend to do something with a database in a message handler, and you want the handling of the incoming message to function as a "unit of work", then you might want to check out this page :)
You can read Fowler's ubiquitous description if you're interested in the details, but it basically boils down to this: You want to do stuff - either fully, or not at all.
Usually, when your "stuff" is "doing work in a database", you will use that database and its ability to
- start a transaction before you start doing stuff
- do stuff as part of the transaction
- commit the transaction if all goes well
- roll back the transaction if something fails
These things can of course be implemented in an ad-hoc fashion whenever you need it, but it's usually better implemented by hooking into Rebus in the right places - this is what we'll talk about here.
Rebus receives each message inside an ITransactionContext which can always be accessed somehow. The transaction context has an Items dictionary that we can use to stash things for the duration of handling a message, and it has a way of enlisting actions to be executed at the right time.
In this example, we're using Castle Windsor as the IoC container, and we want to make an Entity Framework database context called MyDatabaseContext.
All serious IoC containers can resolve instances by using some kind of factory method, and we'll use that mechanism to set up injection of MyDatabaseContext by having the GetDatabaseContext method called like this:
container.Register(
Component.For<MyDatabaseContext>()
.UsingFactoryMethod(k => GetDatabaseContext(), managedExternally: true)
.LifestyleTransient()
);
Note that since Windsor has a default lifestyle of SINGLETON, we need to specify LifestyleTransient in order to have the method called each time it needs to be resolved. Moreover, since Windsor is so helpful, we take over tracking by setting managedExternally: true.
And then, the actual logic to get the database context and hook it up with Rebus' transaction context goes like this:
MyDatabaseContext GetDatabaseContext()
{
// this method must never be called outside of a Rebus message handler
var transactionContext = AmbientTransactionContext.Current;
if (transactionContext == null) {
throw new InvalidOperationException("Tried to get tx context outside of msg handler!");
}
return transactionContext
.GetOrAdd("current-my-database-context", () => {
var database = new MyDatabaseContext();
// when the context is first created, it must be enlisted in the commit..
transactionContext.OnCommitted(async () => {
await database.SaveChangesAsync();
});
// ...and dispose callbacks on the tx context
transactionContext.OnDisposed(() => {
database.Dispose();
});
});
}
A more detailed example showing off Rebus with Castle Windsor and some raw ADO.NET can be found in the Unit of work sample from the Rebus samples repository.
Basic stuff
- Home
- Introduction
- Getting started
- Different bus modes
- How does rebus compare to other .net service buses?
- 3rd party extensions
- Rebus versions
Configuration
Scenarios
Areas
- Logging
- Routing
- Serialization
- Pub sub messaging
- Process managers
- Message context
- Data bus
- Correlation ids
- Container adapters
- Automatic retries and error handling
- Message dispatch
- Thread safety and instance policies
- Timeouts
- Timeout manager
- Transactions
- Delivery guarantees
- Idempotence
- Unit of work
- Workers and parallelism
- Wire level format of messages
- Handler pipeline
- Polymorphic message dispatch
- Persistence ignorance
- Saga parallelism
- Transport message forwarding
- Testing
- Outbox
- Startup/shutdown
Transports (not a full list)
Customization
- Extensibility
- Auto flowing user context extensibility example
- Back off strategy
- Message compression and encryption
- Fail fast on certain exception types
Pipelines
- Log message pipelines
- Incoming messages pipeline
- Incoming step context
- Outgoing messages pipeline
- Outgoing step context
Prominent application services