MassTransit in ASP.NET Core: A Practical Guide to Event-Driven .NET

Leader 24 58 119
calendar_todayschedule2 min read

MassTransit in ASP.NET Core: A Practical Guide to Event-Driven .NET
Intro

Event-driven architectures help teams decouple services, scale independently, and handle failures gracefully. MassTransit is a mature, open-source library that makes message-based workflows in .NET straightforward. This guide shows a minimal but production-ready setup in ASP.NET Core with RabbitMQ.

1) Install packages

From your Web API project:

dotnet add package MassTransit
dotnet add package MassTransit.RabbitMQ

(For Azure Service Bus use MassTransit.Azure.ServiceBus.Core instead.)

2) Define a message contract

Contracts should be versionable and live in a shared project.

public interface SubmitOrder
{
    Guid OrderId { get; }
    string CustomerId { get; }
    DateTime Timestamp { get; }
}

3) Create a consumer

using MassTransit;

public sealed class SubmitOrderConsumer : IConsumer<SubmitOrder>
{
    public async Task Consume(ConsumeContext<SubmitOrder> context)
    {
        var msg = context.Message;
        // Your domain logic here (idempotent!)
        Console.WriteLine($"Received SubmitOrder {msg.OrderId} for {msg.CustomerId}");
        await Task.CompletedTask;
    }
}

4) Configure MassTransit in Program.cs (.NET 9 minimal hosting)

RabbitMQ example with retries and health checks.

using MassTransit;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddMassTransit(cfg =>
{
    cfg.SetKebabCaseEndpointNameFormatter();

    cfg.AddConsumer<SubmitOrderConsumer>(c =>
    {
        // optional: configure consumer-level retry, etc.
    });

    cfg.UsingRabbitMq((context, bus) =>
    {
        bus.Host("rabbitmq", h =>
        {
            h.Username("guest");
            h.Password("guest");
        });

        bus.ReceiveEndpoint("submit-order-queue", e =>
        {
            e.ConfigureConsumeTopology = false; 
            e.ConfigureConsumer<SubmitOrderConsumer>(context);

            e.UseMessageRetry(r => r.Interval(3, TimeSpan.FromSeconds(5)));
            e.PrefetchCount = 16;
            e.ConcurrentMessageLimit = 8;
        });
    });
});

builder.Services.AddHealthChecks();

var app = builder.Build();
app.MapHealthChecks("/health");
app.MapGet("/", () => "OK");
app.Run();

Docker tip

If you run RabbitMQ locally via Docker:

docker run -d --hostname rabbit \
  -p 5672:5672 -p 15672:15672 \
  --name rabbitmq rabbitmq:3-management

UI is at http://localhost:15672 (guest/guest).

5) Publish a message (from a Controller or Service)

Inject IPublishEndpoint for pub/sub or ISendEndpointProvider for point-to-point.

using MassTransit;

public sealed class OrderAppService
{
    private readonly IPublishEndpoint _publish;
    public OrderAppService(IPublishEndpoint publish) => _publish = publish;

    public Task SubmitAsync(Guid orderId, string customerId)
        => _publish.Publish<SubmitOrder>(new
        {
            OrderId = orderId,
            CustomerId = customerId,
            Timestamp = DateTime.UtcNow
        });
}

Or via an endpoint (send to a specific queue):

public sealed class OrderSender
{
    private readonly ISendEndpointProvider _send;
    public OrderSender(ISendEndpointProvider send) => _send = send;

    public async Task SendAsync(Guid orderId, string customerId)
    {
        var endpoint = await _send.GetSendEndpoint(new Uri("queue:submit-order-queue"));
        await endpoint.Send<SubmitOrder>(new { OrderId = orderId, CustomerId = customerId, Timestamp = DateTime.UtcNow });
    }
}

6) Error handling, retries, and observability

Retries: use UseMessageRetry on endpoints or the bus. Prefer bounded retries with intervals or exponential backoff.

Poison messages: failed messages after retries land in _error queues automatically.

Health checks: expose /health and rely on container orchestration to restart unhealthy pods.

Idempotency: make consumers safe to reprocess (e.g., check a processed table or use dedup keys).

7) (Optional) Outbox & transactions

If you publish events within a DB transaction, consider an outbox pattern (MassTransit integrates with EFCore Outbox) to avoid dual-write issues and ensure at-least-once delivery without duplicates.

8) Azure Service Bus variant (quick sketch)

Swap the transport:

builder.Services.AddMassTransit(cfg =>
{
    cfg.AddConsumer<SubmitOrderConsumer>();

    cfg.UsingAzureServiceBus((context, bus) =>
    {
        bus.Host(builder.Configuration["ASB_CONNECTION"]!);

        bus.SubscriptionEndpoint<SubmitOrder>("submit-order-sub", e =>
        {
            e.ConfigureConsumer<SubmitOrderConsumer>(context);
            e.MaxConcurrentCalls = 8;
        });
    });
});

Conclusion

MassTransit keeps the happy path simple while giving you the tools for serious systems: retries, sagas, scheduling, observability, and transport flexibility. Start minimal, add policies as you learn your failure modes, and keep consumers idempotent.

7.8k Points201 Badges24 58 119
Athens Greece
43Posts
163Comments
205Followers
94Connections
Passionate about building robust and scalable software solutions with a focus on .NET technologies. With extensive experience in leading teams, designing systems, and mentoring dev... Show more
Build your own developer journey
Track progress. Share learning. Stay consistent.

2 Comments

0 votes
0 votes
🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

The Sovereign Vault — A Comprehensive Guide to Protocol-Driven AI

Ken W. Algerverified - Jun 4

Building a Pub/Sub System in .NET: MassTransit, Reactive Extensions, and BlockingCollection

Spyros - Aug 26, 2025

Building an Order Processing Saga with MassTransit

Spyros - Aug 12, 2025

Decorate services in ASP.NET Core, step by step

Spyros - Nov 14, 2025

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

6 comments
2 comments
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!