With .NET Core now becoming mainstream, you might ask how you can run NServiceBus endpoints in an asp.net process. After all, it sounds pretty trivial, but turns out maybe not as easy as it sounds. Let’s see why.

Integration

Running On Startup

The code to start the endpoint would typically have two steps. The first step is to setup the endpoint using ‘EndpoingConfiguration’ object and the second step is to create, or potentially ‘start’ an endpoint. In NSB v6 and higher, the create and start are both an async method. Initialization of the endpoint during the startup of the WebHost / Generic Host would look like this:

1
2
3
4
5
6
7
8
9
10
11
12
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

EndpointConfiguration endpointConfig = CreateEndpointConfiguration(); //<-- configure the endpoint here

IEndpointInstance endpoint = Endpoint.Start(endpointConfig).GetAwaiter().GetResult();
}
}

As it is obvious in the code above, you’d have to block the Start method call as it is async. It goes without saying that this is not a good practice and could cause issues like thread getting blocked or even deadlocks in certain situations.

More importantly, if you have message handlers in your process, when you start the endpoint it opens the flood gates and the handler will start receiving messages. This might be undesired as you are still initializing the application. The ConfigureServices stage is for preparing your services and not actually run anything.

Running async tasks

The proper way to run long running async tasks in aspnet core is to use IHostedService interface. The good thing about this interface is that it supports async already as both StartAsync and StopAsync methods are designed to support async method calls.

Starting NServiceBus as a background service would look something like this (this is a simplified version):

Note: there is a base class called BackgroundService implementing this interaface already, so you can inherit from that base class instead.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class NServiceBusServiceHost : IHostedService
{
EndpointConfiguration _config;
TaskCompletionSource<IEndpointInstance> _endpointTcs;

public NServiceBusServiceHost(EndpointConfiguration config)
{
_config = config;
_endpointTcs = new TaskCompletionSource<IEndpointInstance>(TaskCreationOptions.RunContinuationsAsynchronously);
}

public Task<IEndpointInstance> EndpointInstance => _endpointTcs.Task;

public async Task StartAsync(CancellationToken cancellationToken)
{
try
{
var endpoint = await Endpoint.Start(_config).ConfigureAwait(false);
_endpointTcs.TrySetResult(endpoint);
}
catch (Exception e)
{
_endpointTcs.TrySetException(e);
throw;
}
}

public async Task StopAsync(CancellationToken cancellationToken)
{
var endpoint = await EndpointInstance.ConfigureAwait(false);
await endpoint.Stop().ConfigureAwait(false);
}
}

And then we can easily plug in the hosted service in our Startup method like:

1
services.AddHostedService<NServiceBusServiceHost>();

And this, of course does not work. The problem is that the service expects the EndpointConfiguration object to be registered in the container. An alternative to that is to manually register the hosted service like so:

1
services.AddSingleton<IHostedService>(provider => new NServiceBusServiceHost(CreateEndpointConfiguration()));

There are still other things to figure out though. For example, how do you send messages in your controllers? With this approach, you’ll need to inject the hosted service into your controller to use the IEndpointInstance interface. If your controller has direct dependency on IEndpointInstance (or IMessageSession) to send messages, it won’t work.

Using the adapter

As you can see, making it work is a lot of chore and figuring out all the little details would add up. Luckily, there are two packages to use. Why two packages, you may ask? We’ll get to that in a bit. Using these packages allow seamless integration between Asp.net Core and NServiceBus. The package essentially does the same thing as here, but also registers IMessageSession in the container as well. Your code would look like this:

1
services.AddNServiceBus(CreateEndpointConfiguration());

There are two packages to choose from to achieve this. There is the Community.NServiceBus.WebHost package, designed for .NET Core 2.x and there is the official NServiceBus.Extensions.Hosting that works for .NET Core 3.x. Why two packages, again? The reason is that there is race-condition in Asp.net Core 2.x which would cause IMessageSession not being available when the application is being initialized. The community package has a workaround for this to ‘make it work’ but the better option is to use the official package but you’ll need to be on .NET Core 3.x to do so.

The official documentation can be found online.