DotNet Dispatcher is a lightweight, compile-time generated CQRS dispatcher for .NET. By leveraging Roslyn code generation, this framework eliminates runtime reflection, making it highly AOT-friendly and performance-efficient.
With DotNet Dispatcher, developers can define commands and queries with their corresponding handlers, and the dispatcher is automatically generated at compile time.
✅ Zero Runtime Reflection - Uses Roslyn to generate code at compile time.
✅ AOT-Friendly - Ideal for .NET Native AOT scenarios.
✅ Lightweight - No additional runtime dependencies or complexity.
✅ Explicit yet Automated Wiring - Just define your handlers, and the generator does the rest.
✅ Fast & Efficient - No runtime service lookups; everything is wired at compile time.
# Clone the repository
$ git clone https://github.com/psimsa/dotnet-dispatcher.git
$ cd dotnet-dispatcher
# Add to your .NET project
$ dotnet add package DotNetDispatcherCreate a command by implementing ICommand<TResponse> and a corresponding handler:
public record PlaceOrderCommand(string ProductId, int Quantity) : ICommand<OrderResponse>;
public class PlaceOrderHandler : ICommandHandler<PlaceOrderCommand, OrderResponse>
{
public Task<OrderResponse> Handle(PlaceOrderCommand command)
{
return Task.FromResult(new OrderResponse(Guid.NewGuid(), "Order Placed Successfully"));
}
}public record GetOrderQuery(Guid OrderId) : IQuery<OrderResponse>;
public class GetOrderHandler : IQueryHandler<GetOrderQuery, OrderResponse>
{
public Task<OrderResponse> Handle(GetOrderQuery query)
{
return Task.FromResult(new OrderResponse(query.OrderId, "Order Retrieved"));
}
}After compilation, the dispatcher is automatically generated and can be injected like this:
public class OrderService
{
private readonly IDispatcher _dispatcher;
public OrderService(IDispatcher dispatcher)
{
_dispatcher = dispatcher;
}
public async Task<OrderResponse> PlaceOrderAsync(string productId, int quantity)
{
return await _dispatcher.Send(new PlaceOrderCommand(productId, quantity));
}
}- Roslyn Source Generator scans for commands and queries marked with
[GenerateDispatcher]. - The dispatcher implementation is generated at compile time, avoiding reflection-based dependency resolution.
- Aside from the generated dispatcher, the generator also creates an extension method for
IServiceCollectioncalledRegisterCommandDispatcherAndHandlersthat ensures necessary dependencies are automatically registered in DI, allowing easy injection and usage. Simply callservices.RegisterCommandDispatcherAndHandlers()during your DI setup.
| Feature | MediatR | DotNet Dispatcher |
|---|---|---|
| Reflection-Free | ❌ Uses runtime reflection | ✅ No runtime reflection |
| AOT-Friendly | ❌ May have issues | ✅ Fully compatible |
| Performance | ✅ Optimized compile-time wiring | |
| Setup | ✅ Easy | ✅ Easy (via source generator) |
You can customize the dispatcher behavior by modifying the source generator logic or extending handler interfaces.
We welcome contributions! Please feel free to:
- Open issues for bugs or feature requests.
- Submit PRs to enhance functionality or improve documentation.
This project is licensed under the MIT License.
Maintained by psimsa.