Search code examples
c#.netinversion-of-controlautofacnservicebus

Chicken and egg issue implementing Autofac with NServicebus


Background

I'm trying to set up a Web API 2 which needs to communicate to a NServicebus Endpoint.

I will need to implement IoC, which will be done using Autofac.

What I have

A controller defined like so:

[RoutePrefix("api")]
public class Controller : ApiController
{
    private IEndpointInstance EndpointInstance { get; set; }

    public public MyController(IEndpointInstance endpointInstance)
    {
        this.EndpointInstance = endpointInstance;
    }

    [HttpGet]
    [Route("dostuff")]
    public async Task DoStuff()
    {
        var command = new MyCommand
        {
            ...
        };

        await this.EndpointInstance.SendLocal(command);
    }
}

And in global.asax

Application_Start

protected async void Application_Start()
{
    GlobalConfiguration.Configure(WebApiConfig.Register);

    await RegisterNServiceBusWithAutofac();
}

RegisterNServiceBusWithAutofac

private async Task RegisterNServiceBusWithAutofac()
{
    var builder = new ContainerBuilder();

    var endpointConfiguration = await GetEndpointConfiguration("My.Service");

    var endpointInstance = await Endpoint.Start(endpointConfiguration);

    builder.RegisterInstance(endpointInstance);

    var container = builder.Build();

    endpointConfiguration.UseContainer<AutofacBuilder>(c => c.ExistingLifetimeScope(container));

    builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
}

GetEndpointConfiguration

private static async Task<EndpointConfiguration> GetEndpointConfiguration(string name)
{
    var endpointConfiguration = new EndpointConfiguration(name);

    // Set transport.
    var routing = endpointConfiguration.UseTransport<MsmqTransport>().Routing();

    // Register publish to self
    routing.RegisterPublisher(typeof(EventHasFinished), name);

    endpointConfiguration.UseSerialization<JsonSerializer>();
    endpointConfiguration.UsePersistence<InMemoryPersistence>();
    endpointConfiguration.SendFailedMessagesTo("error");
    endpointConfiguration.EnableInstallers();

    return endpointConfiguration;
}

The result

I get the following error on the UseContainer line:

Unable to set the value for key: NServiceBus.AutofacBuilder+LifetimeScopeHolder. The settings has been locked for modifications. Move any configuration code earlier in the configuration pipeline

What I think this means

I think I need to do all Autofac registrations for the NServicebus when creating the endpointConfiguration. The above manipulates the builder instance after that.

But

I can't do the above, because I need to register the endpointinstance to the IoC, because I need that in my controller to send messages. And that doesn't exist yet, because I need the endpointConfiguration first, for that.

So I have a chicken and egg situation ...

Question

Do I understand the issue correctly and how can I solve it while making sure that IoC works correctly for the Controller?

I.e.: this.EndpointInstance has been correctly instantiated through IoC.


Solution

  • Instead of registering the actual instance, you could register it with a lambda expression that is going to be executed the first time the container will be asked to resolve IEndpointInstance.

    builder
        .Register(x =>
        {
            var endpointConfiguration = GetEndpointConfiguration("My.Service").GetAwaiter().GetResult();
            var endpointInstance = Endpoint.Start(endpointConfiguration).GetAwaiter().GetResult();
    
            return endpointInstance
        })
        .As<IEndpointInstance>()
        .SingleInstance();