EndpointWithExternallyManagedServiceProvider together with ExtensionsLoggerFactory

If I let NSB use my IOC Container (Microsoft IServiceCollection) as in the sample here

… how is it recommended/possible to also let it use my ILoggerFactory?

NServiceBus.Logging.LogManager.UseFactory(new ExtensionsLoggerFactory(_loggerFactory));

(chicken and egg problem)

Hi @Stig_Christensen,

Before going into details, have you considered using the Generic Host with the corresponding NServiceBus package instead of using the EndpointWithExternallyManagedServiceProvider API directly? The Generic Host integration is typically much easier to use and in this case, also handles the logging integration with the logger configured on the host. See NServiceBus.Extensions.Hosting • Particular Docs for more details. I’d definitely recommend to use the generic host integration instead of EndpointWithExternallyManagedServiceProvider in almost all cases.

The NServiceBus.Extensions.Logging doesn’t have built-in support to setup the LogManager without the DI available, so you’d have to copy over the DeferredLogger implementation from the generic host support package: NServiceBus.Extensions.Hosting/src/NServiceBus.Extensions.Hosting/Logging at master · Particular/NServiceBus.Extensions.Hosting · GitHub

Yes I see, mush easier.

Is there a way, to run the configuration (UseNServiceBus) and let NSB scan for handlers, but not start the actual endpoint? It is in our test suite, where I do not have RabbitMQ, but still want to get a MessageHandler from the IOC container.

We are using AspNetCore WebApplicationFactory for testing

@Stig_Christensen, can you give me more details about the type of tests you’re running and what you’re trying to verify?

Plain simple, we do a lot of accept test and is testing with the database. So often I like to get a service (read MessageHandler) from the IOC container and process a message into (with a TestableMessageSession). I assert what happens to the database.

Before when using EndpointWithExternallyManagedServiceProvider the NSB configuration (and MessageHandler scanning) was separated from the actually endpoint start up, hence the IOC container was populated with the MessageHandlers. The test suite was configured not to start the endpoint.

Now with UseNServiceBus the startup is not handled in my code, so the test suite with fail with missing RabbitMQ server.

1 Like

Is there a way, to run the configuration (UseNServiceBus) and let NSB scan for handlers, but not start the actual endpoint?

Yes, you can build the generic host without starting it and it will provide you a IServiceProvider that can resolve message handlers without the endpoint being started:

Host.CreateDefaultBuilder(args)
      .UseNServiceBus(context =>
      {
           // ...
      })
      .Build();
// don't call Start()

Is your primary need for this the ability to scan the message handler and be able to resolve them from the DI container or are there other functionalities that your test relies on? It might be easier to handle the scanning and handler registration separately if that’s all you need.

Also, since you mentioned acceptance tests, have you considered running your endpoint with the learning transport instead and fully starting it, so that you get more actual behavior of the endpoint (like executing the full message handling pipeline)?

learning transport, might be handy later, but actually the handlers are very primitive and does not send messages. Also tests are run in parallel, could be fixed with different learning transport configurations.

.Start() does a lot important stuff like running EF migrations and start HTTP endpoints, so this is nice to execute from test suite, and WebApplicationFactory ( Microsoft.AspNetCore.Mvc.Testing ) does it implicit.

Actually I would also like the production start up to not fail if RabbitMQ is down. As it is now (again with UseNServiceBus) my Windows Service will fail on start up if RMQ is down. Before with EndpointWithExternallyManagedServiceProvider my service started gracefully and retries (Polly) would eventually connect

As I wrote Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory will always start your builder (since it runs your Program.cs).

So actually it is not possible to not start NSB when using UseNServiceBus. Only alternative is to check environment (e.g. Development) and add learning tranpost instead, right? I don’t put test code in my production code, so what to do then?

So actually it is not possible to not start NSB when using UseNServiceBus. Only alternative is to check environment (e.g. Development) and add learning tranpost instead, right? I don’t put test code in my production code, so what to do then?

My comment regarding just using Build was based on your described need to only resolve handlers from the DI container to invoke them manually, which would be possible with that. If you want to start the endpoint normally, then the generic host needs to be started, yes.

I think it might be easiest to just “copy” the endpoint configuration (or extract it into a shared class/method) and then overwrite the transport settings with the learning transport in the acceptance test-specific UseNserviceBus configuration.
The host is really just a host and not an existential part of your application logic, so it should be ok to use a different host for your testing needs.

learning transport, might be handy later, but actually the handlers are very primitive and does not send messages. Also tests are run in parallel, could be fixed with different learning transport configurations.

If you just want to test your fairly simple handlers, I think it would be a lot easier to just go with “unit tests” (the name might be misleading). Creating the instances, managing the dependencies etc. for testing purpose will be more straight forward (see Testing NServiceBus • Testing • Particular Docs). If your goal is to run with acceptance or integration tests, the NServiceBus endpoint should be fully started and handlers should be invoked by sending the message via the transport.

I don’t see how I can change statup configuration when using Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory

… without doing some static state hack.

WebApplicationFactory will start Program as in production.

It would be nice to be able to control when NSB connects to the transport. I would use this both from test suite and in the production environment

We currently don’t have an out-of-the-box solution for this when using the WebApplicationFactory as NServiceBus can’t be configured directly via IConfiguration APIs yet. So you’d have to support switching transports in your actually application via config or compilation flags and then use the WebApplicationFactory configuration options to switch the configuration.