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