-
-
Notifications
You must be signed in to change notification settings - Fork 364
Container adapters
Unless you're working with Rebus in one-way client mode (as described on Different bus modes), Rebus will be receiving messages and will at some point need to obtain a message handler instance to handle a message.
Rebus will do that by asking the IHandlerActivator abstraction for handlers for the incoming message, and this is usually where you would use an implementation of that wraps an IoC container, a so-called "container adapter", to create the handler instances by delegating the instantiation to the IoC container.
This way, handlers can have dependencies injected, instance lifetimes managed, etc.
In order to configure Rebus to use a container adapter, include the NuGet package of the container of your choice (e.g. Rebus.Castle.Windsor) and go
Configure.With(new CastleWindsorContainerAdapter(container))
.(...)
This will do the following two things:
- Pass the container adapter to Rebus, thus delegating handler instantiation to the container.
- Put the resulting
IBus
instance into the container with a singleton lifestyle, ensuring that the bus is disposed when the container is disposed.
When your application shuts down, you would have done this anyway:
container.Dispose();
Just keep doing it, because the container adapter ensures that the IBus
instance is registered in a way that disposes the bus properly when the container is disposed, allowing currently executing handlers to finish gracefully, etc.
At some point, Rebus had a registration API for automatically picking up and registering handlers in the container, but it was extremely hard to normalize the behavior across all the available IoC containers. Therefore: you're on your own when it comes to registering handlers.
It shouldn't be too hard, though - I'm not an expert on all IoC containers, but with Castle Windsor you'd usually be satisfied doing something like this:
container.Register(Classes.FromAssemblyContaining<SomeHandler>()
.BasedOn<IHandleMessages>()
.WithServiceAllInterfaces()
.LifestyleTransient());
in order to automagically pick up all Rebus handlers from one particular assembly.
When you're working with sagas and you have multiple worker threads, it is important that your handlers have a transient lifestyle! In other words, it is important that every incoming message will result in a fresh handler instance.
This is because of the Data
property on the handler - this is where the current saga instance will be put, and if your handler instance is a singleton it will be shared between threads, most likely leading to the hardest-to-track-down bugs you'll ever not track down.
You know what? Just forget about the "when this and when that" I said above - just make sure, always, that your handlers are registered with a transient lifestyle! - in general, transient lifestyle is a better default for IoC container components, and that is a fact.
Since you may not need a full-blown IoC container, and since you may be wishing for a more lightweight way of handling messages, Rebus has a built-in container adapter.
It's not an IoC container, so it's a little different - one aspect that's different, is that it implements IDisposable
- this means that the adapter can be disposed, which you should do when your application shuts down.
You can dispose the IBus
too - basically, just make sure you either dispose the IBus
or the BuiltinHandlerActivator
.
Here's an example on how it can be used:
using(var activator = new BuiltinHandlerActivator())
{
Configure.With(activator)
.Transport(t => t.UseMsmq("inputQueue"))
.Start();
// here's the bus
var bus = activator.Bus;
// publish greetings forever
while(true)
{
bus.Publish(Console.ReadLine()).Wait();
}
}
If you want to handle messages with BuiltinHandlerActivator
, you have a couple of options - first, you can register a handler factory:
activator.Register(() => new SomeHandler(pass, dependencies, into, ctor));
The Register
method will reflect on the inferred handler type and figure out which messages it can handler.
Lastly, there's the inline handler:
activator.Handle<SomeMessage>(async msg => Console.WriteLine("Got message: {0}", msg.Text));
which can be neat to use in simple scenarios. If you need the bus and/or the message context with the inline handler, there' two overloads for that:
activator.Handle<SomeRequest>(async (bus, msg) => await bus.Reply(new SomeReply()));
activator.Handle<SomeRequest>(async (bus, context, msg) => {
var headers = context.TransportMessage.Headers;
var returnAddress = headers[Headers.ReturnAddress];
await bus.Reply(new SomeReply(string.Format("hello {0}", returnAddress)));
});
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