An Introduction to MediatR in ASP.NET Applications

You may be here because your Dev Manager / Senior Dev / Technical Architect keeps mentioning this library but haven’t quite explained what it is or why you should use it or how exactly to use it. Maybe, you first started looking at MediatR’s own documentation and then quickly tumbled into so many rabbit holes – What is the Mediator Design Pattern? Or more generally, what are Design Patterns? WTH is GoF? What is messaging? What is loose coupling? Why do I need thin controller action methods? Why do I need all these interfaces?

While all those questions are valid and important ones to software development, they shouldn’t serve as a barrier to entry to you using this great library (or the pattern that it models) that you already know at a high-level provides a way to architect your dotnet applications in a clean manner. Here, in this introductory post, let’s quickly jump into an example of how to wire this library up in your ASP.NET apps and how to create a controller that utilizes this pattern so that you understand the basic mechanics of it before pondering on the more philosophical points of how this library lends itself to you writing clean code.

Setup Project

For this example, I’m utilizing a brand new ASP.NET application that I generated via the dotnet CLI using the command dotnet new webapi. Once the application is created, let’s download and install the MediatR package from NuGet:

dotnet add package MediatR.Extensions.Microsoft.DependencyInjection

This package is specifically built for ASP.NET projects using the built-in Dependency Injection (DI) container. Installing this will also install the MediatR base package. With these packages in place, you can wire this up in your Program.cs like so:

using System.Reflection;
using MediatR;

var builder = WebApplication.CreateBuilder(args);

// Add other services here

// Add MediatR here
builder.Services.AddMediatR(Assembly.GetExecutingAssembly());

var app = builder.Build();

// do other initializations here

app.Run();

MediatR – A Basic Usage Example

The “webapi” template that we just used to create this project comes with an example controller named WeatherController.cs. This controller has a GET action method – GetWeatherForecast – that returns a made up five-day weather forecast. Let’s rewrite this using the mediator pattern with the help of MediatR. The basic usage of this pattern dictates that we create the following artifacts to handle each individual request/response scenario:

  • a message (or request)
  • a handler
  • a response

Remember – that’s the basic scenario. MediatR also has provisions for handling other scenarios such as sending a message to multiple handlers; sending a message/command that doesn’t require any response and other use-cases.

Let’s create request, handler, and response classes for our GetWeatherForecast endpoint.

using MediatR;

namespace mediatr_intro;
public class WeatherForecastRequest : IRequest<WeatherForecastResponse>
{
    // your request params will go here
}

public class GetWeatherForecastHandler : IRequestHandler<WeatherForecastRequest, WeatherForecastResponse>
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    public async Task<WeatherForecastResponse> Handle(WeatherForecastRequest request, CancellationToken cancellationToken)
    {
        var weatherForecastResponse = new WeatherForecastResponse
        {
            WeatherForecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
        };
        return await Task.FromResult(weatherForecastResponse);
    }
}

public class WeatherForecastResponse
{
    public IEnumerable<WeatherForecast> WeatherForecasts { get; set; } = new List<WeatherForecast>();
}

With this refactoring, our Controller simply becomes a dispatcher devoid of any business logic (which is exactly what we want to achieve by using this pattern/library).

using MediatR;
using Microsoft.AspNetCore.Mvc;

namespace mediatr_intro.Controllers;

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private readonly IMediator mediator;

    public WeatherForecastController(IMediator mediator)
    {
        this.mediator = mediator;
    }

    [HttpGet(Name = "GetWeatherForecast")]
    public async Task<WeatherForecastResponse> Get()
    {
        return await mediator.Send(new WeatherForecastRequest());
    }
}

Why Should I Use This Pattern?

While the subject of clean architecture is a bit beyond the scope of this introductory blog post, I wanted to give you a quick summary of why you want to adopt this pattern and library in your ASP.NET applications:

It provides you with an effective way to separate your business logic from the ASP.NET framework code. As a result of our refactoring above, we were able to extract and encapsulate all our business logic (i.e., all the code that makes a weather forecast) into a separate file. Our controller (which is a framework level construct with framework level concerns such as authentication, authorization, HTTP verbs, HTTP responses, etc.) thus became devoid of any business logic. Such separation allows us to reason with our business level code in an isolated matter. It also allows us to unit-test such code without framework level concerns getting in the way.

Closing Remarks

You can check out the companion repository for this post on my GitHub account, here:

tvaidyan/mediatr-intro: Companion repo to my “An Introduction to MediatR in ASP.NET Applications” blog post on tvaidyan.com (github.com)

Leave a Comment

Your email address will not be published. Required fields are marked *