NativeMediator 1.0.4

dotnet add package NativeMediator --version 1.0.4
                    
NuGet\Install-Package NativeMediator -Version 1.0.4
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NativeMediator" Version="1.0.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NativeMediator" Version="1.0.4" />
                    
Directory.Packages.props
<PackageReference Include="NativeMediator" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NativeMediator --version 1.0.4
                    
#r "nuget: NativeMediator, 1.0.4"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NativeMediator@1.0.4
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NativeMediator&version=1.0.4
                    
Install as a Cake Addin
#tool nuget:?package=NativeMediator&version=1.0.4
                    
Install as a Cake Tool

NativeMediator Logo

NativeMediator

A high-performance mediator pattern library for .NET, optimized for Native AOT and serverless functions (AWS Lambda, Azure Functions).

NuGet License

✨ Features

  • 🚀 Native AOT Compatible - Designed from the ground up for ahead-of-time compilation
  • High Performance - Uses ValueTask for 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 IAsyncEnumerable streaming 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

  1. Use explicit registration instead of assembly scanning:

    // ✅ AOT-compatible
    options.AddHandler<MyRequest, MyResponse, MyHandler>();
    
    // ❌ Not AOT-compatible (uses reflection)
    options.AddHandlersFromAssemblyContaining<Program>();
    
  2. Use concrete types in your handlers and avoid dynamic type creation.

  3. Avoid generic open types in pipeline behaviors when targeting AOT.

  4. 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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.

Version Downloads Last Updated
1.0.4 192 2/3/2026
1.0.3 518 1/21/2026
1.0.2 50 1/21/2026
1.0.0 48 1/21/2026