Understanding MediatR Assembly Registration in .NET

posted 3 min read

Introduction
When building clean, modular applications in .NET using the MediatR library, developers often encounter this configuration line:

builder.Services.AddMediatR(typeof(Application.Assembly.AssemblyReference).Assembly);

For newcomers to MediatR, this syntax can appear cryptic. However, it serves a critical role in enabling the framework to function correctly. This article provides a comprehensive explanation of assembly registration in MediatR, covering its purpose, implementation, and best practices.
What Is MediatR?
MediatR is a lightweight, in-process messaging library for .NET that facilitates decoupling between request senders (such as controllers) and request handlers (such as services or command processors). It implements several important patterns:

**- Command Query Responsibility Segregation (CQRS)

  • Mediator pattern
  • Pipeline behaviors for cross-cutting concerns
    The library works by defining requests (commands or queries) and their corresponding handlers, then routing requests through a mediator to the appropriate handler.
    Understanding Assembly Registration
    The Core Concept**
    MediatR requires explicit assembly registration to discover and register your handlers in the dependency injection container. The framework cannot automatically scan every assembly in your application—you must specify which assemblies contain your MediatR handlers.

    builder.Services.AddMediatR(typeof(SomeTypeInTargetAssembly).Assembly);
    Assembly Location: typeof(SomeTypeInTargetAssembly).Assembly provides MediatR with access to the target assembly
    Handler Discovery: MediatR uses reflection to scan the assembly for types implementing MediatR interfaces (IRequestHandler<,>, INotificationHandler<>, etc.)
    Service Registration: Discovered handlers are automatically registered with the dependency injection container
    Why Assembly Registration Is Required
    MediatR operates through dependency injection and reflection, but it cannot magically locate your handlers across all loaded assemblies. Without proper assembly registration, you will encounter this runtime exception:

    System.InvalidOperationException: No service for type 'IRequestHandler<YourCommand, YourResponse>' has been registered.
    This error occurs because the dependency injection container cannot resolve the required handler service.
    Implementation Best Practices
    Using a Marker Class

    The recommended approach is to create a dedicated marker class within your handler assembly:
    Application/Assembly/AssemblyReference.cs

    namespace Application.Assembly
    {

     public class AssemblyReference 
     {
         // This class serves as an assembly marker for MediatR registration
     }
    

    }

Program.cs (or Startup.cs)

builder.Services.AddMediatR(typeof(Application.Assembly.AssemblyReference).Assembly);

This approach offers several advantages:

  • Maintainability: Creates a stable reference point that won't change

  • Clarity: Makes the registration intent explicit

  • Decoupling: Avoids tight coupling to specific handler classes
    Here's a typical Clean Architecture project structure:

    Solution/
    ├── Api/ # Presentation layer
    │ └── Program.cs # Application entry point
    ├── Application/ # Application layer
    │ ├── Features/
    │ │ ├── Authentication/
    │ │ │ ├── LoginCommand.cs
    │ │ │ └── LoginCommandHandler.cs
    │ │ └── Users/
    │ │ ├── GetUserQuery.cs
    │ │ └── GetUserQueryHandler.cs
    │ └── Assembly/
    │ └── AssemblyReference.cs # Marker class
    ├── Domain/ # Domain layer
    └── Infrastructure/ # Infrastructure layer

An Example
Defining a Command and Handler
LoginCommand.cs

    using MediatR;

namespace Application.Features.Authentication;

public class LoginCommand : IRequest<LoginResponse>
{
    public string Username { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
}

public class LoginResponse
{
    public bool IsSuccess { get; set; }
    public string Token { get; set; } = string.Empty;
    public string Message { get; set; } = string.Empty;
}

Using the Handler in a Controller

[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
    private readonly IMediator _mediator;

    public AuthController(IMediator mediator)
    {
        _mediator = mediator;
    }

    [HttpPost("login")]
    public async Task<ActionResult<LoginResponse>> Login([FromBody] LoginCommand command)
    {
        var response = await _mediator.Send(command);
        return Ok(response);
    }
}

Conclusion
Proper assembly registration is fundamental to MediatR's operation in .NET applications. By understanding how AddMediatR(typeof(...).Assembly) works and following established best practices, you can build maintainable, decoupled applications that leverage MediatR's powerful messaging capabilities.

Key Takeaways

Always register the assembly containing your MediatR handlers
Use marker classes for clean, maintainable assembly references
Ensure proper project references between your API and Application layers
Consider pipeline behaviors for implementing cross-cutting concerns

Next Steps
To further enhance your MediatR implementation:

Implement validation using FluentValidation with MediatR pipeline behaviors
Add logging and monitoring through custom pipeline behaviors
Explore notifications using INotification and INotificationHandler<>
Consider performance optimization with caching behaviors
Implement error handling strategies using pipeline behaviors

If you read this far, tweet to the author to show them you care. Tweet a Thanks

Nice clear explanation of MediatR assembly registration! A quick note: some code snippets aren’t properly formatted as code blocks, which makes them a bit harder to read—wrapping those parts in proper markdown code fences (`csharp) would really help.

Also, adding H1/H2 headers could improve navigation and make the guide easier to skim.

How do you usually organize and test your handlers to keep everything maintainable as your app grows?

Thanks for the feedback i will definitely look through it again

More Posts

What's New in .NET Aspire 9.1

Barret Blake - Mar 13

Understanding the Observer Pattern in C# with IObservable and IObserver

Spyros - Mar 11

Suitable DB Choice in USSD Application

Moses Korir - Mar 12

Program does not contain a static 'main' method suitable for an entry point

Rafael Borges Batist - Feb 23, 2024

When NOT to Use AsSplitQuery() in EF.core

Spyros - Feb 26
chevron_left