# Configuration

MassTransit can be used in any .NET application, however, the application type can influence the bus configuration. Several different application type examples are shown below, each of which lists any additional dependencies are required. Each example focuses on simplicity, and therefore may omit certain extra features to avoid confusion.

NOTE

Except where required by the application type, no dependency injection container is used. For container configuration examples, refer to the containers section.

# Console application

A console application has a Main entry point, which is part of the Program.cs class by default. The example below configures a simple bus instance that publishes an event with a value entered.

References: MassTransit.RabbitMQ

namespace EventPublisher
{
    using MassTransit;

    public interface ValueEntered
    {
        string Value { get; }
    }

    public class Program
    {
        // be sure to set the C# language version to 7.3 or later
        public static async Task Main()
        {
            var busControl = Bus.Factory.CreateUsingRabbitMq(cfg => cfg.Host("localhost"));

            // Important! The bus must be started before using it!
            await busControl.StartAsync();
            try
            {
                do
                {
                    string value = await Task.Run(() =>
                    {
                        Console.WriteLine("Enter message (or quit to exit)");
                        Console.Write("> ");
                        return Console.ReadLine();
                    });

                    if("quit".Equals(value, StringComparison.OrdinalIgnoreCase))
                        break;

                    await busControl.Publish<ValueEntered>(new
                    {
                        Value = value
                    });
                }
                while (true);
            }
            finally
            {
                await busControl.StopAsync();
            }
        }
    }
}

In the example, the bus is configured and started after which a publishing loop allows values to be entered and published. When the loop exits, the bus is stopped.

REMEMBER

Always start the bus before using it.

# ASP.NET Core

MassTransit integrates natively with ASP.NET Core, and supports:

  • Hosted service to start and stop the bus following the application lifecycle
  • Registers the bus instance as a singleton for the required interfaces
  • Adds health checks for the bus instance and receive endpoints
  • Configures the bus to use the host process ILoggerFactory instance

If you want to register your consumers in the ASP.NET Core service collection, use the following code as an example:

References: MassTransit.AspNetCore, MassTransit.RabbitMQ

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHealthChecks();
        services.AddMvc();
        
        // Consumer dependencies should be scoped
        services.AddScoped<SomeConsumerDependency>()

        services.AddMassTransit(x =>
        {
            x.AddConsumer<OrderConsumer>();

            x.AddBus(context => Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                // configure health checks for this bus instance
                cfg.UseHealthCheck(context);

                cfg.Host("rabbitmq://localhost");

                cfg.ReceiveEndpoint("submit-order", ep =>
                {
                    ep.PrefetchCount = 16;
                    ep.UseMessageRetry(r => r.Interval(2, 100));

                    ep.ConfigureConsumer<OrderConsumer>(context);
                });
            }));
        });

        services.AddMassTransitHostedService();
    }
}

Remember, however, that it is perfectly fine to set up all the required dependencies in the Startup, which serves as the bootstrap code for your application. By doing that you can avoid weird cases when you have two dependencies that implement the same interface and then you cannot properly register them both, since only the last one will count.

To make the health checks work, remember to add this line to the Configure method in the Startup.cs file. The endpoints.MapControllers() call is included by default, the MapHealthChecks is the only addition required.

public void Configure(IApplicationBuilder app)
{
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();

        // The readiness check uses all registered checks with the 'ready' tag.
        endpoints.MapHealthChecks("/health/ready", new HealthCheckOptions()
        {
            Predicate = (check) => check.Tags.Contains("ready"),
        });

        endpoints.MapHealthChecks("/health/live", new HealthCheckOptions()
        {
            // Exclude all checks and return a 200-Ok.
            Predicate = (_) => false
        });
    });
}

To enable a separate readiness check from the health check, the services can be configured to separate the two.

 services.Configure<HealthCheckPublisherOptions>(options =>
{
    options.Delay = TimeSpan.FromSeconds(2);
    options.Predicate = (check) => check.Tags.Contains("ready");
});

Also, as an obvious requirement, you need to use .NET Core 2.2 (or higher) and have those two package references in your project file:

<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />

Of course, health checks only work when you provide an endpoint that is accessible via http or https. That implies that you cannot do that using the Microsoft.NET.Sdk, so you need to ensure that your csproj file starts with this line:

<Project Sdk="Microsoft.NET.Sdk.Web">

The Web SDK is used by default if you are using the ASP.NET Core Web Application template, but you can also change it, if you used the Console Application template. In such a case you'd also need to add some more packages to be able to host the http endpoint using Kestrel. The easiest way to migrate a console application to the web application (that still runs as a console application) by creating a new web application with the type Empty and checking the differences.

# Windows service

A Windows service is recommended for consuming commands and events as it provides an autonomous execution environment for message consumers. The service can be started and stopped using the service control manager, as well as monitored by operations tools.

TIP

To create a Windows service, we strongly recommend using Topshelf, as it was built specifically for this purpose. Topshelf is easy to use, has zero dependencies, and creates a service that can be self-installed without additional tools.

The important aspect of configuring a bus in a Windows service is to ensure that the bus is only configured and started when the service is started.

namespace EventService
{
    using MassTransit;
    using Topshelf;

    public class Program
    {
        public static int Main()
        {
            return (int)HostFactory.Run(cfg => cfg.Service(x => new EventConsumerService()));
        }
    }

    class EventConsumerService :
        ServiceControl
    {
        IBusControl _bus;

        public bool Start(HostControl hostControl)
        {
            _bus = ConfigureBus();
            _bus.Start();

            return true;
        }

        public bool Stop(HostControl hostControl)
        {
            _bus?.Stop(TimeSpan.FromSeconds(30));

            return true;
        }

        IBusControl ConfigureBus()
        {
            return Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                cfg.Host("localhost");

                cfg.ReceiveEndpoint("event_queue", e =>
                {
                    e.Handler<ValueEntered>(context =>
                        Console.Out.WriteLineAsync($"Value was entered: {context.Message.Value}"));
                })
            });
        }
    }
}

# Web application

Configuring a bus in a web site is typically done to publish events, send commands, as well as engage in request/response conversations. Hosting receive endpoints and persistent consumers is not recommended (use a service as shown above).

# ASP.NET (MVC/WebApi2)

In a web application, the HttpApplication class methods of Application_Start and Application_End are used to configure/start the bus and stop the bus respectively.

While many MassTransit samples use Topshelf, web applications are an exception where the standard web application conventions are followed.

public class MvcApplication : HttpApplication
{
    static IBusControl _busControl;

    public static IBus Bus
    {
        get { return _busControl; }
    }

    protected void Application_Start()
    {
        _busControl = ConfigureBus();
        _busControl.Start();
    }

    protected void Application_End()
    {
        _busControl.Stop(TimeSpan.FromSeconds(10));;
    }

    IBusControl ConfigureBus()
    {
        return Bus.Factory.CreateUsingRabbitMq(cfg =>
        {
            cfg.Host("localhost");
        });
    }
}

public class NotifyController : Controller
{
    public async Task<ActionResult> Put(string value)
    {
        await MvcApplication.Bus.Publish<ValueNotified>(new
        {
            Value = value
        });

        return View();
    }
}

public class CommandController : Controller
{
    public async Task<ActionResult> Send(string value)
    {
        var endpoint = await MvcApplication.Bus.GetSendEndpoint(_serviceAddress);

        await endpoint.Send<SubmitValue>(new
        {
            Timestamp = DateTime.UtcNow,
            Value = value
        });

        return View();
    }
}

The above example is kept simple, providing a static MvcApplication.Bus property to access the bus instance (for publishing events, and sending commands to endpoints). Newer version of ASP.NET have built-in dependency resolution, in which case the IBus should be registered so that controllers can specify the dependency in the constructor. In fact, the inherited IPublishEndpoint and ISendEndpointProvider should also be registered.

The example controllers show how to publish and send messages as well.

# OWIN Pipeline (WebApi2)

WebApi2 can alternatively use the OWIN pipeline. But OWIN doesn't have Application_End and so it requires a different approach to ensure the bus is disposed properly.

This example shows an OWIN WebApi2 project using the Autofac Container. The concept should be similar if you aren't using WebApi2 or a different Container, but still OWIN.

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // STANDARD WEB API SETUP:

        // Get your HttpConfiguration. In OWIN, you'll create one
        // rather than using GlobalConfiguration.
        var config = new HttpConfiguration();

        WebApiConfig.Register(config); // Register your routes

        // Make the autofac container
        var builder = new ContainerBuilder();

        // Register your Bus
        builder.Register(c => Bus.Factory.CreateUsingRabbitMq(sbc => sbc.Host("localhost","dev")))
            .As<IBusControl>()
            .As<IBus>()
            .As<IPublishEndpoint>()
            .As<ISendEndpointProvider>()
            .SingleInstance();

        // Register anything else you might need...

        // Build the container
        var container = builder.Build();

        // OWIN WEB API SETUP:

        // set the DependencyResolver
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

        // Register the Autofac middleware FIRST, then the Autofac Web API middleware,
        // and finally the standard Web API middleware.

        app.UseAutofacMiddleware(container);
        app.UseAutofacWebApi(config);
        app.UseWebApi(config);

        // Starts Mass Transit Service bus, and registers stopping of bus on app dispose
        var bus = container.Resolve<IBusControl>();
        var busHandle = bus.Start();

        var properties = new AppProperties(app.Properties);

        if(properties.OnAppDisposing != CancellationToken.None)
        {
            properties.OnAppDisposing.Register(() => busHandle.Stop(TimeSpan.FromSeconds(30)));
        }
    }
}

The important bit is the last several lines where we resolve the IBusControl and register an action to stop when the app disposes.

You'll also notice we registered the bus .As<IPublishEndpoint>(). That's because we are following the guidance above, where our websites should really only be Publishing to the service bus.

Now your controller might look like:

public class MyController : ApiController
{
    private readonly IPublishEndpoint _publishEndpoint;

    public MyController(IPublishEndpoint publishEndpoint)
    {
        _publishEndpoint = publishEndpoint;
    }

    [HttpPost]
    public async Task<IHttpActionResult> OrderShipped(int orderId)
    {
        await _publishEndpoint.Publish<OrderShipped>(
        new
        {
            OrderId = orderId
        });

        return Ok("Success!");
    }
}