Search code examples
c#castle-windsorrebus

Publishing to multiple Rebus queues with different stores


We have one application that publishes messages - PublishApp.

The messages (MessageA and MessageB) are picked up by two different consumer apps (ConsumerA and ConsumerB).

All applications use SQL Server as a transport and Windsor configuration, but the two Consumers have different databases in SQL Server.

How can we configure PublishApp to publish MessageA for ConsumerA and MessageB for ConsumerB?

I have tried using DetermineMessageOwnership as described here, but that doesn't seem to actually be called (no breakpoints hit). I'm a little mystified as to what the string endpoints returned should be.

I was hoping that I could set up an IBus component in Windsor with a specific name, then reference that by name when setting up my MessageB-publishing class. However it's not clear how to set up an IBus in Windsor outside of the magic box that does it all for me.

Fiddling with Windsor configuration leads me to a Windsor error if I try to call Configure.With(new WindsorContainerAdapter(container)) twice, as it is interpreted as registering the IBus interface twice. I can't see an extension point here to give one of the IBus instances a name, and hence differentiate them in Windsor.

Alternatively, trying to reuse the Configure.With... call throws an error telling me I have called .Transport() on the configurer twice, which is also not allowed (but which would let me use a different connection string...)

Adding XML configuration will let me specify different endpoints for my different messages, but not different SQL connection strings.

What I would really like to end up with is something like:

// Set up Bus A
var busA = Configure.With(new WindsorContainerAdapter(container))
    .Transport(tc => tc.UseSqlServerInOneWayClientMode("ConnectionStringA"))
    .Subscriptions(sc => sc.StoreInSqlServer("ConnectionStringA", "RebusSubscriptions"))
    .CreateBus()
    .Start();

// Set up Bus B
var busB = Configure.With(new WindsorContainerAdapter(container))
    .Transport(tc => tc.UseSqlServerInOneWayClientMode("ConnectionStringB"))
    .Subscriptions(sc => sc.StoreInSqlServer("ConnectionStringB", "RebusSubscriptions"))
    .CreateBus()
    .Start();

// Register Bus A in Windsor
container.Register(Component.For<IBus>()
    .Named("BusA")
    .Instance(busA));

// Register a class that depends on IBus, and set it to use Bus A
container.Register(Component.For<IPublishA>()
    .ImplementedBy<PublishA>()
    .DependsOn(Dependency.OnComponent(typeof(IBus), "BusA"));

// And a registration also for IBus B, and for IPublishB to reference named "BusB"

Note: I do not want to listen to multiple buses, only publish events to them. Other applications are monitoring the queues, and each application only listens for one event on one queue.


Solution

  • We resolved this in the end by dropping the WindsorContainerAdaptor. Since we're not handling any messages, only publishing/sending, we don't need any of the 'handler' stuff in the container adaptor and we can switch the registration of the IBus component around to happen outside of the configuration/starting, rather than inside it. This gives us the control to name the IBus registration.

        public static void ConfigureAndStartBus(IWindsorContainer container)
        {
            _RegisterBus(container, "ConnectionStringA" "BusA");
            _RegisterBus(container, "ConnectionStringB" "BusB");
        }
    
        private static void _RegisterBus(IWindsorContainer container, string connectionString, string busName)
        {
            var bus = Configure.With(new BuiltinContainerAdapter())
                .Transport(tc => tc.UseSqlServerInOneWayClientMode(connectionString))
                .Subscriptions(sc => sc.StoreInSqlServer(connectionString, "RebusSubscriptions"))
                .CreateBus()
                .Start();
    
            container.Register(
                Component.For<IBus>()
                    .Named(busName)
                    .LifestyleSingleton()
                    .Instance(bus));
        }
    

    Then in class PublishA, we can register it with a dependency on BusA, and PublishB can be registered with a dependency on BusB. The messages go to separate databases, and are picked up by separate subscribers to do work in those different databases.