NativeMediator 1.0.4
dotnet add package NativeMediator --version 1.0.4
NuGet\Install-Package NativeMediator -Version 1.0.4
<PackageReference Include="NativeMediator" Version="1.0.4" />
<PackageVersion Include="NativeMediator" Version="1.0.4" />
<PackageReference Include="NativeMediator" />
paket add NativeMediator --version 1.0.4
#r "nuget: NativeMediator, 1.0.4"
#:package NativeMediator@1.0.4
#addin nuget:?package=NativeMediator&version=1.0.4
#tool nuget:?package=NativeMediator&version=1.0.4
NativeMediator
A high-performance mediator pattern library for .NET, optimized for Native AOT and serverless functions (AWS Lambda, Azure Functions).
✨ Features
- 🚀 Native AOT Compatible - Designed from the ground up for ahead-of-time compilation
- ⚡ High Performance - Uses
ValueTaskfor reduced allocations - 🔌 Dependency Injection - First-class integration with
Microsoft.Extensions.DependencyInjection - 📦 Zero Dependencies - Only depends on DI abstractions
- 🔄 Pipeline Behaviors - Cross-cutting concerns like logging, validation, and retry
- 📡 Streaming Support - Built-in support for
IAsyncEnumerablestreaming requests - 🎯 CQRS Ready - Perfect for Command Query Responsibility Segregation patterns
📦 Installation
dotnet add package NativeMediator
🚀 Quick Start
1. Define a Request and Handler
using NativeMediator;
// Request with response
public record GetUserQuery(int UserId) : IRequest<UserDto>;
// Handler
public class GetUserQueryHandler : IRequestHandler<GetUserQuery, UserDto>
{
public ValueTask<UserDto> Handle(GetUserQuery request, CancellationToken cancellationToken = default)
{
// Your logic here
return new ValueTask<UserDto>(new UserDto(request.UserId, "John Doe"));
}
}
public record UserDto(int Id, string Name);
2. Register Services (AOT-Compatible)
For Native AOT compatibility, register handlers explicitly:
using NativeMediator;
services.AddNativeMediator(options =>
{
// Explicit registration (AOT-friendly)
options.AddHandler<GetUserQuery, UserDto, GetUserQueryHandler>();
});
3. Send Requests
public class MyService
{
private readonly IMediator _mediator;
public MyService(IMediator mediator)
{
_mediator = mediator;
}
public async Task<UserDto> GetUser(int userId)
{
return await _mediator.Send(new GetUserQuery(userId));
}
}
📚 Usage Patterns
Requests Without Response (Commands)
public record CreateUserCommand(string Name, string Email) : IRequest;
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand>
{
public ValueTask<Unit> Handle(CreateUserCommand request, CancellationToken cancellationToken = default)
{
// Create user logic
return Unit.ValueTask;
}
}
// Registration
options.AddHandler<CreateUserCommand, CreateUserCommandHandler>();
// Usage
await mediator.Send(new CreateUserCommand("John", "john@example.com"));
Notifications (Pub/Sub)
Notifications are delivered to multiple handlers:
public record UserCreatedNotification(int UserId, string Email) : INotification;
public class SendWelcomeEmailHandler : INotificationHandler<UserCreatedNotification>
{
public ValueTask Handle(UserCreatedNotification notification, CancellationToken cancellationToken = default)
{
// Send welcome email
return ValueTask.CompletedTask;
}
}
public class UpdateAnalyticsHandler : INotificationHandler<UserCreatedNotification>
{
public ValueTask Handle(UserCreatedNotification notification, CancellationToken cancellationToken = default)
{
// Update analytics
return ValueTask.CompletedTask;
}
}
// Registration
options.AddNotificationHandler<UserCreatedNotification, SendWelcomeEmailHandler>();
options.AddNotificationHandler<UserCreatedNotification, UpdateAnalyticsHandler>();
// Usage
await mediator.Publish(new UserCreatedNotification(1, "john@example.com"));
Streaming Requests
For returning multiple items asynchronously:
public record GetLogsQuery(DateTime From, DateTime To) : IStreamRequest<LogEntry>;
public class GetLogsQueryHandler : IStreamRequestHandler<GetLogsQuery, LogEntry>
{
public async IAsyncEnumerable<LogEntry> Handle(
GetLogsQuery request,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
// Stream logs from database
await foreach (var log in _database.StreamLogsAsync(request.From, request.To, cancellationToken))
{
yield return log;
}
}
}
// Registration
options.AddStreamHandler<GetLogsQuery, LogEntry, GetLogsQueryHandler>();
// Usage
await foreach (var log in mediator.CreateStream(new GetLogsQuery(DateTime.Today, DateTime.Now)))
{
Console.WriteLine(log);
}
🔧 Pipeline Behaviors
Pipeline behaviors allow you to add cross-cutting concerns around request handling:
Built-in Behaviors
Logging Behavior
using NativeMediator.Behaviors;
options.AddBehavior<MyRequest, MyResponse, LoggingBehavior<MyRequest, MyResponse>>();
Validation Behavior
using NativeMediator.Behaviors;
// Implement IValidator<TRequest>
public class CreateUserValidator : IValidator<CreateUserCommand>
{
public ValueTask<ValidationResult> ValidateAsync(
CreateUserCommand request,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(request.Name))
{
return new ValueTask<ValidationResult>(
ValidationResult.Failure(new ValidationError("Name", "Name is required")));
}
return new ValueTask<ValidationResult>(ValidationResult.Success);
}
}
// Register validator and behavior
services.AddScoped<IValidator<CreateUserCommand>, CreateUserValidator>();
options.AddBehavior<CreateUserCommand, Unit, ValidationBehavior<CreateUserCommand, Unit>>();
Retry Behavior
using NativeMediator.Behaviors;
// Custom retry behavior
services.AddScoped<IPipelineBehavior<MyRequest, MyResponse>>(sp =>
new RetryBehavior<MyRequest, MyResponse>(
maxRetries: 3,
delay: TimeSpan.FromSeconds(1),
shouldRetry: ex => ex is HttpRequestException));
Exception Handling Behavior
using NativeMediator.Behaviors;
services.AddScoped<IPipelineBehavior<MyRequest, MyResponse>>(sp =>
new ExceptionHandlingBehavior<MyRequest, MyResponse>(
(request, ex, ct) => new ValueTask<MyResponse>(new MyResponse { Error = ex.Message })));
Custom Pipeline Behavior
public class TimingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly ILogger<TimingBehavior<TRequest, TResponse>> _logger;
public TimingBehavior(ILogger<TimingBehavior<TRequest, TResponse>> logger)
{
_logger = logger;
}
public async ValueTask<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken = default)
{
var stopwatch = Stopwatch.StartNew();
try
{
return await next();
}
finally
{
stopwatch.Stop();
_logger.LogInformation(
"Request {RequestName} took {ElapsedMs}ms",
typeof(TRequest).Name,
stopwatch.ElapsedMilliseconds);
}
}
}
☁️ Serverless Examples
AWS Lambda
public class Function
{
private readonly IMediator _mediator;
public Function()
{
var services = new ServiceCollection();
services.AddNativeMediator(options =>
{
options.MediatorLifetime = ServiceLifetime.Singleton;
options.AddHandler<ProcessOrderCommand, OrderResult, ProcessOrderHandler>();
});
var provider = services.BuildServiceProvider();
_mediator = provider.GetRequiredService<IMediator>();
}
public async Task<APIGatewayProxyResponse> FunctionHandler(
APIGatewayProxyRequest request,
ILambdaContext context)
{
var command = JsonSerializer.Deserialize<ProcessOrderCommand>(request.Body);
var result = await _mediator.Send(command);
return new APIGatewayProxyResponse
{
StatusCode = 200,
Body = JsonSerializer.Serialize(result)
};
}
}
Azure Functions (Isolated Worker)
var host = new HostBuilder()
.ConfigureFunctionsWebApplication()
.ConfigureServices(services =>
{
services.AddNativeMediator(options =>
{
options.AddHandler<ProcessOrderCommand, OrderResult, ProcessOrderHandler>();
});
})
.Build();
host.Run();
public class OrderFunction
{
private readonly IMediator _mediator;
public OrderFunction(IMediator mediator)
{
_mediator = mediator;
}
[Function("ProcessOrder")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req)
{
var command = await req.ReadFromJsonAsync<ProcessOrderCommand>();
var result = await _mediator.Send(command);
var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(result);
return response;
}
}
⚙️ Configuration Options
services.AddNativeMediator(options =>
{
// Mediator lifetime (default: Scoped)
options.MediatorLifetime = ServiceLifetime.Scoped;
// Default handler lifetime (default: Scoped)
options.DefaultHandlerLifetime = ServiceLifetime.Scoped;
// Explicit handler registration (AOT-compatible)
options.AddHandler<MyRequest, MyResponse, MyHandler>();
options.AddHandler<MyCommand, MyCommandHandler>(); // For IRequest (void)
options.AddNotificationHandler<MyNotification, MyNotificationHandler>();
options.AddStreamHandler<MyStreamRequest, MyItem, MyStreamHandler>();
// Pipeline behaviors
options.AddBehavior<MyRequest, MyResponse, MyBehavior>();
// Assembly scanning (NOT AOT-compatible, use for JIT scenarios only)
options.AddHandlersFromAssemblyContaining<Program>();
});
🎯 Native AOT Best Practices
Use explicit registration instead of assembly scanning:
// ✅ AOT-compatible options.AddHandler<MyRequest, MyResponse, MyHandler>(); // ❌ Not AOT-compatible (uses reflection) options.AddHandlersFromAssemblyContaining<Program>();Use concrete types in your handlers and avoid dynamic type creation.
Avoid generic open types in pipeline behaviors when targeting AOT.
Test with PublishAot enabled during development:
<PropertyGroup> <PublishAot>true</PublishAot> </PropertyGroup>
🔄 Comparison with Other Libraries
| Feature | NativeMediator | MediatR | WolverineFx |
|---|---|---|---|
| Native AOT | ✅ First-class | ⚠️ Limited | ⚠️ Limited |
| ValueTask | ✅ Yes | ❌ Task | ✅ Yes |
| Serverless Optimized | ✅ Yes | ❌ No | ❌ No |
| Pipeline Behaviors | ✅ Yes | ✅ Yes | ✅ Yes |
| Streaming | ✅ Yes | ✅ Yes | ✅ Yes |
| Notifications | ✅ Yes | ✅ Yes | ✅ Yes |
| Dependencies | Minimal | Minimal | Many |
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📮 Support
- 📧 Create an issue on GitHub
- ⭐ Star this repository if you find it useful!
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0-preview.1.25080.5)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on NativeMediator:
| Package | Downloads |
|---|---|
|
NativeLambdaRouter
A high-performance API Gateway routing library for AWS Lambda, optimized for Native AOT. Provides declarative route mapping to mediator commands with path parameter extraction, health checks, and automatic error handling. |
|
|
Native.FluentValidation
High-performance fluent validation library built for .NET 10 Native AOT. No reflection, no expression trees, no runtime scanning. |
|
|
Native.FluentValidation.NativeLambdaMediator
Native AOT-friendly validation integration for AWS Lambda using NativeMediator. |
GitHub repositories
This package is not used by any popular GitHub repositories.