Below is my working example demonstrating this issue. I tried to be as concise as possible. You should be able to drag this into a new .NET Console App, install these three packages:
<package id="NServiceBus" version="7.2.1" targetFramework="net462" />
<package id="NServiceBus.Autofac" version="7.0.0" targetFramework="net462" />
<package id="NServiceBus.RabbitMQ" version="5.1.2" targetFramework="net462" />
And adjust the connection strings to your suiting. I created two brand new, empty VHosts for this experiment. To get NSB to create the queues and exchanges, run it once with the two “SendOnly” lines commented out, and then put them back in for the actual test. This just saves me from having to also write a consumer for this demo.
Here’s how it works, you run it, it configures the endpoints, and then DI is used to generate an instance of a service, which exposes both endpoint instances (through clever hiding, to work around the chicken-or-the-egg DI problem with NSB) and sends a message specific to each endpoint.
Whenever the lines that say blah.UseContainer<AutofacBuilder>
are uncommented, the program works correctly. Messages are put into the queues and there’s no issues. If you uncomment those lines in each endpoint section and then run it, curiously you’ll receive this error message:
(coming from the send call for dwEp/ep2msg.)
Note how the message says “DataWarehouse” exchange, but in the “WF” queue. This is the bug. Or maybe bug, I don’t know, but something definitely gets whacked out when DI is used for both.
using Autofac;
using NServiceBus;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DemoForSupport
{
class Program
{
public static async Task Main(string[] args)
{
//Set up DI
Bootstrapper.Initialize();
//Create endpoint 1
var endpointConfig = new EndpointConfiguration("Workflow");
endpointConfig.SendOnly(); //Run once with this commented out in both endpoints to set them up
endpointConfig.AuditProcessedMessagesTo("audit");
endpointConfig.EnableInstallers();
var transport = endpointConfig.UseTransport<RabbitMQTransport>();
transport.UseConventionalRoutingTopology();
transport.ConnectionString("host=rabbit1;virtualhost=WF;username=admin;password=password;RequestedHeartbeat=100;");
var routing = transport.Routing();
routing.RouteToEndpoint(typeof(Endpoint1Message), "Workflow");
endpointConfig.License(ConfigurationManager.AppSettings["License"]);
endpointConfig.UseContainer<AutofacBuilder>(customizations => { customizations.ExistingLifetimeScope(Bootstrapper.Container); });
EndpointConfigurator.EndpointInstance = await Endpoint.Start(endpointConfig).ConfigureAwait(false);
Console.WriteLine("Endpoint 1 set up");
//Create endpoint 2
var endpointConfig2 = new EndpointConfiguration("DataWarehouse");
endpointConfig2.SendOnly(); //Run once with this commented out in both endpoints to set them up
endpointConfig2.EnableInstallers();
endpointConfig2.License(ConfigurationManager.AppSettings["License"]);
var transport2 = endpointConfig2.UseTransport<RabbitMQTransport>();
transport2.UseDirectRoutingTopology();
transport2.ConnectionString("host=rabbit1;virtualhost=DW;username=admin;password=password;RequestedHeartbeat=100;");
var routing2 = transport2.Routing();
routing2.RouteToEndpoint(typeof(Endpoint2Message), "DataWarehouse");
endpointConfig2.UseContainer<AutofacBuilder>(customizations => { customizations.ExistingLifetimeScope(Bootstrapper.Container); });
var instance2 = await Endpoint.Start(endpointConfig2).ConfigureAwait(false);
EndpointConfigurator.DatawarehouseEndpointInstance = new DataWarehouseEndpointInstance() { EndpointInstance = instance2 };
Console.WriteLine("Endpoint 2 set up");
do
{
Console.WriteLine("Doing stuff.. press X to exit.");
using (var lts = Bootstrapper.Container.BeginLifetimeScope())
{
var svc = lts.Resolve<IThingySvc>();
await svc.DoStuff();
}
} while (Console.ReadKey().Key != ConsoleKey.X);
}
}
public static class Bootstrapper
{
public static IContainer Container { get; private set; }
public static void Initialize()
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ThingySvc>().AsImplementedInterfaces().InstancePerLifetimeScope();
builder.Register(x => EndpointConfigurator.EndpointInstance).As<IEndpointInstance>().SingleInstance();
builder.Register(x => EndpointConfigurator.DatawarehouseEndpointInstance).As<IDataWarehouseEndpointInstance>().SingleInstance();
Container = builder.Build();
Console.WriteLine("Bootstrapper (DI) set up");
}
}
public interface IThingySvc { Task DoStuff(); }
public class ThingySvc : IThingySvc
{
private IEndpointInstance ep;
private IEndpointInstance dwEp;
public ThingySvc(IEndpointInstance ep, IDataWarehouseEndpointInstance dwEp)
{
this.ep = ep;
this.dwEp = dwEp.EndpointInstance;
}
public async Task DoStuff()
{
//Send messages to each EP.
Endpoint1Message ep1msg = new Endpoint1Message();
ep1msg.Data = Guid.NewGuid();
await ep.Send(ep1msg).ConfigureAwait(false);
Endpoint2Message ep2msg = new Endpoint2Message();
ep2msg.Data = Guid.NewGuid();
await dwEp.Send(ep2msg).ConfigureAwait(false);
}
}
public interface IDataWarehouseEndpointInstance
{
IEndpointInstance EndpointInstance { get; set; }
}
public class DataWarehouseEndpointInstance : IDataWarehouseEndpointInstance
{
public IEndpointInstance EndpointInstance { get; set; }
}
public class EndpointConfigurator
{
public static IEndpointInstance EndpointInstance { get; set; }
public static IDataWarehouseEndpointInstance DatawarehouseEndpointInstance { get; set; }
}
public class Endpoint1Message : IMessage
{
public Guid Data { get; set; }
}
public class Endpoint2Message : IMessage
{
public Guid Data { get; set; }
}
}