Blazingly fast, convention-based C# mediator powered by source generators and interceptors.
- 🚀 Near-direct call performance - Zero runtime reflection, minimal overhead
- ⚡ Convention-based - No interfaces or base classes required
- 🔧 Full DI support - Microsoft.Extensions.DependencyInjection integration
- 🧩 Plain handler classes - Drop in static or instance methods anywhere
- 🎪 Middleware pipeline - Before/After/Finally hooks with state passing
- 🎯 Built-in Result<T> - Rich status handling without exceptions
- 🔄 Tuple returns - Automatic cascading messages
- 🔒 Compile-time safety - Early validation and diagnostics
- 🧪 Easy testing - Plain objects, no framework coupling
- 🐛 Superior debugging - Short, simple call stacks
dotnet add package Foundatio.Mediator
// Program.cs
services.AddMediator();
// Messages (records, classes, anything)
public record GetUser(int Id);
public record CreateUser(string Name, string Email);
public record UserCreated(int UserId, string Email);
// Handlers - just plain classes ending with "Handler" or "Consumer"
public class UserHandler
{
public async Task<Result<User>> HandleAsync(GetUser query, IUserRepository repo)
{
var user = await repo.FindAsync(query.Id);
return user ?? Result.NotFound($"User {query.Id} not found");
}
public async Task<(User user, UserCreated evt)> HandleAsync(CreateUser cmd, IUserRepository repo)
{
var user = new User { Name = cmd.Name, Email = cmd.Email };
await repo.AddAsync(user);
// Return tuple: first element is response, rest are auto-published
return (user, new UserCreated(user.Id, user.Email));
}
}
// Event handlers
public class EmailHandler
{
public async Task HandleAsync(UserCreated evt, IEmailService email)
{
await email.SendWelcomeAsync(evt.Email);
}
}
// Middleware - classes ending with "Middleware"
public class LoggingMiddleware(ILogger<LoggingMiddleware> logger)
{
public Stopwatch Before(object message) => Stopwatch.StartNew();
public void Finally(object message, Stopwatch sw, Exception? ex)
{
logger.LogInformation("Handled {MessageType} in {Ms}ms",
message.GetType().Name, sw.ElapsedMilliseconds);
}
}
// Query with response
var result = await mediator.InvokeAsync<Result<User>>(new GetUser(123));
if (result.IsSuccess)
Console.WriteLine($"Found user: {result.Value.Name}");
// Command with automatic event publishing
var user = await mediator.InvokeAsync<User>(new CreateUser("John", "john@example.com"));
// UserCreated event automatically published to EmailHandler
// Publish events to multiple handlers
await mediator.PublishAsync(new UserCreated(user.Id, user.Email));
Key topics:
- Getting Started - Step-by-step setup
- Handler Conventions - Discovery rules and patterns
- Middleware - Pipeline hooks and state management
- Result Types - Rich status handling
- Performance - Benchmarks vs other libraries
- Configuration - MSBuild and runtime options
Contributions are welcome! Please feel free to submit a Pull Request. See our documentation for development guidelines.
MIT License