In my previous post about the ready-made pipeline step injector, I showed how to easily inject a new pipeline step into the pipeline at a position relative to another step.
A concrete example of doing this could be the Rebus.TransactionScope extension, which wraps the execution of handlers in a .NET System.Transactions.TransactionScope.
Let’s take this one from the outside-in. I want a configuration experience that looks like this:
1 2 3 4 5 6 7 |
Configure.With(activator) .Transport(t => t.UseInMemoryTransport(new InMemNetwork(), "txtest")) .Options(o => { o.HandleMessagesInsideTransactionScope(); }) .Start(); |
In the example shown, HandleMessagesInsideTransactionScope is an extension method on Rebus’ OptionsConfigurer.
You know about extension methods? They’re great, they allow you to bring in a NuGet package from the outside, which contributes with a function that magically pop up somewhere else, e.g. on a fluent configuration builder thingie – exactly what we need 🙂
From now on, since you’ve read the post about the PipelineStepInjector, it’s so simple that you can probably already imagine what I’m about to show you – here’s the implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public static class TransactionScopeConfigurationExtensions { public static OptionsConfigurer HandleMessagesInsideTransactionScope(this OptionsConfigurer configurer) { configurer.Decorate<IPipeline>(c => { var pipeline = c.Get<IPipeline>(); var stepToInject = new TransactionScopeIncomingStep(); return new PipelineStepInjector(pipeline) .OnReceive(stepToInject, PipelineRelativePosition.Before, typeof (DispatchIncomingMessageStep)); }); return configurer; } } |
As you can see, this is a fairly declarative way of positioning our TransactionScopeIncomingStep right before the message is dispatched (in DispatchIncomingMessageStep).
Only thing left is to show the actual implementation of the step:
1 2 3 4 5 6 7 8 9 10 11 |
class TransactionScopeIncomingStep : IIncomingStep { public async Task Process(IncomingStepContext context, Func<Task> next) { using (var scope = new System.Transactions.TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { await next(); scope.Complete(); } } } |
How about that? 😀
Please note that the Rebus.TransactionScope extension requires at least .NET 4.5.1, because that’s the version of the framework that introduced transaction scopes that would flow to continuations properly when using async/ await.