Search code examples
asp.net-mvcsimple-injectorazure-servicebus-queuesrebus

Rebus multiple Queues based on content


Setup: Rebus in asp.net mvc project using SimpleInjector.

I need to create two handlers which receive messages, each from a specific queue. By following what I have found on this SO answer I have created similar code.

In a class library I have a class which implements SimpleInjector IPackage which have a code like this:

public void RegisterServices( Container container ) {
    container.Register<IHandleMessages<MyMessage>, MyMessageHandler>( Lifestyle.Scoped );
    IContainerAdapter adapter = new SimpleInjectorContainerAdapter( container );
    Configure.With( adapter )
             .Transport( t => t.UseAzureServiceBus( connectionString, "A_QUEUE_NAME", AzureServiceBusMode.Standard ) )
             .Options( oc => {
                 oc.SetNumberOfWorkers( 1 );
                 oc.SimpleRetryStrategy( errorQueueAddress: "A_ERROR_QUEUE_NAME", maxDeliveryAttempts: 3 );
             } )
             .Start();

    Configure.With(adapter
             .Transport(t => t.UseAzureServiceBus(connectionString, "B_QUEUE_NAME")
             .Options( oc => {
                 oc.SetNumberOfWorkers( 1 );
                 oc.SimpleRetryStrategy( errorQueueAddress: "B_ERROR_QUEUE_NAME", maxDeliveryAttempts: 3 );
             } )
             .Start();
}

However when the debugger get to the second Configure.With( ... ) call i terminates with an error saying:

Type IBus has already been registered. If your intention is to resolve a collection of IBus implementations, use the RegisterCollection overloads. More info: https://simpleinjector.org/coll1. If your intention is to replace the existing registration with this new registration, you can allow overriding the current registration by setting Container.Options.AllowOverridingRegistrations to true. More info: https://simpleinjector.org/ovrrd.

Stack trace:

[InvalidOperationException: Type IBus has already been registered. If your intention is to resolve a collection of IBus implementations, use the RegisterCollection overloads. More info: https://simpleinjector.org/coll1. If your intention is to replace the existing registration with this new registration, you can allow overriding the current registration by setting Container.Options.AllowOverridingRegistrations to true. More info: https://simpleinjector.org/ovrrd.]
   SimpleInjector.Internals.NonGenericRegistrationEntry.ThrowWhenTypeAlreadyRegistered(InstanceProducer producer) +102
   SimpleInjector.Internals.NonGenericRegistrationEntry.Add(InstanceProducer producer) +59
   SimpleInjector.Container.AddInstanceProducer(InstanceProducer producer) +105
   SimpleInjector.Container.AddRegistrationInternal(Type serviceType, Registration registration) +69
   SimpleInjector.Container.AddRegistration(Type serviceType, Registration registration) +131
   SimpleInjector.Container.RegisterSingleton(TService instance) +183
   Rebus.SimpleInjector.SimpleInjectorContainerAdapter.SetBus(IBus bus) +55
   Rebus.Config.RebusConfigurer.Start() +2356
   MyModule.RegisterServices(Container container) +497
   SimpleInjector.PackageExtensions.RegisterPackages(Container container, IEnumerable`1 assemblies) +50
   Myproject.SimpleInjectorInitializer.InitializeContainer(Container container) +35
   Myproject.SimpleInjectorInitializer.Initialize() +68
   Myproject.Startup.Configuration(IAppBuilder app) +28

EDIT

I have then removed the second Configure.With( ... ) block of code and now when I do a _bus.Send( message ) I get another error in the consumer process which says

Unhandled exception 1 while handling message with ID fef3acca-97f4-4495-b09d-96e6c9f66c4d: SimpleInjector.ActivationException: No registration for type IEnumerable<IHandleMessages<MyMessage>> could be found. There is, however, a registration for IHandleMessages<MyMessage>; Did you mean to call GetInstance<IHandleMessages<MyMessage>>() or depend on IHandleMessages<MyMessage>? Or did you mean to register a collection of types using RegisterCollection?

Stack Trace:

2017-04-13 10:21:03,805 [77] WARN  Rebus.Retry.ErrorTracking.InMemErrorTracker - 
   at SimpleInjector.Container.ThrowMissingInstanceProducerException(Type serviceType)
   at SimpleInjector.Container.GetInstanceForRootType[TService]()
   at SimpleInjector.Container.GetInstance[TService]()
   at SimpleInjector.Container.GetAllInstances[TService]()
   at Rebus.SimpleInjector.SimpleInjectorContainerAdapter.<GetHandlers>d__3`1.MoveNext()

Solution

  • I usually recommend keeping only one single IBus per container instance, because the bus can considered "an application" in itself, which happens to fit nicely with the fact that an IoC container is an object that can "host" an application for the duration of its lifetime.

    Rebus does not provide a Conforming Container abstraction, because I agree with Mark Seemann that that is a project that is doomed to fail. In fact, as the wiki page mentions, Rebus used to provide automatic registration of handlers, but that turned out to be problematic.

    Instead, Rebus encourages you to provide a "container adapter" (implementation of IContainerAdapter) whose responsibilities are:

    • look up handlers
    • provide a way to register IBus and IMessageContext in The Correct Way

    where container adapters are provided out of the box for Autofac, Castle Windsor, SimpleInjector, etc. However, providing a container adapter is not required – the Configure.With(...) rant is happy with receiving only a "handler activator" (implementation of IHandlerActivator), so if you only want to use your IoC container to look up handlers and take care of registering IBus yourself, you can do that too by implementing IHandlerActivator and looking up handlers in your container.

    TL;DR: The Rebus Way is to treat an instance of your IoC container as a separate application, and therefore it makes sense to register only one IBus in it.

    It is perfectly fine to new up multiple container instances of you want to host multiple applications (or even multiple instances of your application with different message SLAs) in a single process.