A high-performance mediator implementation for .NET 8+ with built-in support for background processing, persistent notifications, and deadlock-safe ConfigureAwait behavior by default. Modern alternative to MediatR with advanced features like compiled expressions, aggressive performance optimizations, automatic UI deadlock prevention, and crash-safe persistence. Supports .NET 9 and .NET 10.
$ dotnet add package SwartBerg.MediatorA mediator implementation for with background processing and notification persistence.
Inspired by MediatR, this library was created as a free alternative with similar patterns but includes built-in persistence and background processing.
The name "SwartBerg" means "Black Mountain" in Afrikaans, it is a combination of my surname and my wife's maiden name. If you like to thank me for the library buy me a coffee. Link is at the bottom of this readme.
Install-Package SwartBerg.Mediatordotnet add package SwartBerg.Mediator<PackageReference Include="SwartBerg.Mediator" Version="2.0.3" />public class GetUserQuery : IRequest<User>
{
public int UserId { get; set; }
}
public class GetUserHandler : IRequestHandler<GetUserQuery, User>
{
public Task<User> Handle(GetUserQuery request, CancellationToken cancellationToken)
{
return Task.FromResult(new User { Id = request.UserId, Name = "John Doe" });
}
}
public class CreateUserCommand : IRequest
{
public string Name { get; set; }
public string Email { get; set; }
}
public class CreateUserHandler : IRequestHandler<CreateUserCommand>
{
public Task Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
public class UserCreatedNotification : INotification
{
public int UserId { get; set; }
public string Name { get; set; }
}
public class SendWelcomeEmailHandler : INotificationHandler<UserCreatedNotification>
{
public Task Handle(UserCreatedNotification notification, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}builder.Services.AddMediator(typeof(Program).Assembly);public class UserController : ControllerBase
{
private readonly IMediator _mediator;
public UserController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet("{id}")]
public async Task<User> GetUser(int id)
{
return await _mediator.Send(new GetUserQuery { UserId = id });
}
[HttpPost]
public async Task CreateUser(CreateUserCommand command)
{
await _mediator.Send(command);
await _mediator.Publish(new UserCreatedNotification { UserId = 1, Name = command.Name });
}
}public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
{
ValidateRequest(request);
return await next();
}
}
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));services.AddSingleton<INotificationPersistence, RedisNotificationPersistence>();
services.AddSingleton<INotificationPersistence, SqlServerNotificationPersistence>();
services.AddMediator(options => options.EnablePersistence = false, typeof(Program).Assembly);services.AddMediator(options =>
{
options.NotificationWorkerCount = 4;
options.EnablePersistence = true;
options.ProcessingInterval = TimeSpan.FromSeconds(30);
options.ProcessingBatchSize = 50;
options.MaxRetryAttempts = 3;
options.InitialRetryDelay = TimeSpan.FromMinutes(2);
options.RetryDelayMultiplier = 2.0;
options.CleanupRetentionPeriod = TimeSpan.FromHours(24);
options.CleanupInterval = TimeSpan.FromHours(1);
// options.UseConfigureAwaitGlobally = false; // only if you need sync ctx
}, typeof(Program).Assembly);Channel-first with optional persistence:
Publish() → Channel → Background Workers
↘
Persist() → Storage
↘
Recovery → Channel
Performance improvements in recent updates:
- Cached concrete handler factories for faster scoped resolution when dispatching notifications.
- Notification handler type caching to avoid holding root-scoped instances and reduce allocations.
- Delayed serialization during Publish to avoid unnecessary serialization when persistence is disabled or fails.
## Testing
```bash
dotnet test
git checkout -b feature/amazing-featuregit commit -m 'Add amazing feature'git push origin feature/amazing-featureMIT License - see LICENSE.
Open issues for bugs or features. Provide clear reproduction steps.
Free forever. If it helps you and you want to buy a coffee:
**Always free. No premium features, no paid support.**Always free.