AotCqrs 1.0.4
dotnet add package AotCqrs --version 1.0.4
NuGet\Install-Package AotCqrs -Version 1.0.4
<PackageReference Include="AotCqrs" Version="1.0.4" />
<PackageVersion Include="AotCqrs" Version="1.0.4" />
<PackageReference Include="AotCqrs" />
paket add AotCqrs --version 1.0.4
#r "nuget: AotCqrs, 1.0.4"
#:package AotCqrs@1.0.4
#addin nuget:?package=AotCqrs&version=1.0.4
#tool nuget:?package=AotCqrs&version=1.0.4
AotCqrs
A blazing fast, reflection-free, AOT-friendly CQRS Mediator library for modern .NET, powered by C# Source Generators.
What is AotCqrs?
AotCqrs is a library that provides a simple way to implement the Mediator and CQRS patterns in your .NET applications, much like the popular MediatR library.
However, its core design principle is performance and compatibility with modern .NET technologies, especially Ahead-Of-Time (AOT) compilation and assembly trimming. It completely avoids runtime reflection by leveraging C# Source Generators to create highly optimized "glue" code at compile time.
This makes it an ideal choice for:
- High-performance ASP.NET Core Minimal APIs.
- Applications targeting NativeAOT.
- Any scenario where startup time and runtime performance are critical.
Key Features
- 🚀 High Performance: Zero runtime reflection. Requests are dispatched via a compile-time generated, highly optimized
switch
expression. - 📦 AOT & Trimming Safe: Designed from the ground up to work flawlessly with .NET's trimming and NativeAOT compilation.
- ✍️ Simple & Familiar API: If you've used MediatR, you'll feel right at home with
IRequest
,IRequestHandler
, andIMediator
. - 🧩 Flexible Handler Registration: Register handlers individually with an attribute, or group them in a central "context" class for better organization.
- 🔗 Powerful Pipeline Behaviors: Intercept and augment request handling with cross-cutting concerns like logging, validation, caching, and transactions using a flexible, contract-based pipeline model.
- ✅ Compile-Time Safety: The source generator connects requests to their handlers and builds the behavior pipeline during compilation, reducing runtime errors.
Why AotCqrs over MediatR?
Feature | MediatR (Reflection-based) | AotCqrs (Source-Generated) |
---|---|---|
Dispatch Mechanism | Runtime Reflection, Assembly Scan | Compile-time generated switch |
Pipeline Behaviors | Runtime DI resolution | Compile-time pipeline weaving |
AOT/Trimming | Can be problematic; requires configuration | Fully Compatible & Safe |
Performance | Good | Excellent / Blazing Fast |
Startup Cost | Scans assemblies on startup | Zero |
Installation
Install the package from NuGet:
dotnet add package AotCqrs
Getting Started: A Four-Step Guide
1. Define your Requests and Handlers
First, create a request (a command or query) and its corresponding handler.
// Features/Products/CreateProduct.cs
using AotCqrs;
// A command is a request that typically returns nothing (Unit).
public record CreateProductCommand(string Name) : IRequest<int>;
// The handler contains the business logic.
// Mark it with [AotCqrsHandler] so the generator can find it.
[AotCqrsHandler]
public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand, int>
{
public Task<int> Handle(CreateProductCommand request, CancellationToken cancellationToken)
{
Console.WriteLine($"---> Creating product: {request.Name}");
// ... your logic here ...
var newProductId = new Random().Next();
return Task.FromResult(newProductId);
}
}
Tip: For better organization, you can also register handlers in bulk using a "context" class. See the "Advanced Handler Registration" section below.
2. Register Services in Program.cs
In your Program.cs
(or wherever you configure services), call the AddAotCqrs()
extension method. The source generator automatically creates this method for you.
// Program.cs
using AotCqrs; // <-- Make sure to add this using directive
var builder = WebApplication.CreateBuilder(args);
// This one line registers the mediator and all discovered handlers and behaviors.
builder.Services.AddAotCqrs();
var app = builder.Build();
// ...
3. Send Requests from your API
Inject IMediator
into your Minimal API endpoints, controllers, or other services and use the Send
method to dispatch requests.
// Program.cs
app.MapPost("/products", async (string name, IMediator mediator) =>
{
var command = new CreateProductCommand(name);
var productId = await mediator.Send(command);
return Results.Ok(new { Id = productId });
});
app.Run();
That's it! You now have a fully functional, high-performance CQRS setup.
Advanced: Pipeline Behaviors
Pipeline behaviors are the most powerful feature of AotCqrs. They allow you to add cross-cutting concerns (logging, validation, transactions, etc.) to your requests in a clean, reusable, and AOT-safe way.
The system is based on "marker interfaces" (contracts). You define what a request is (e.g., ITransactional
), and the pipeline adapts automatically.
Example: Adding Validation and Auditing
Let's build a pipeline that first audits a request and then validates it.
Step 1: Define "Marker Interface" Contracts
Create interfaces that describe the capabilities or requirements of your requests.
// Contracts/IAuditable.cs
public interface IAuditable { }
// Contracts/IValidatable.cs
public interface IValidatable { }
Step 2: Create the Behaviors
Each behavior class implements IPipelineBehavior
and is decorated with [AotCqrsBehavior]
, specifying which contract it targets and its execution order. Lower Order
numbers run first (further out in the pipeline).
// Behaviors/AuditBehavior.cs
using AotCqrs;
using MyWebApp.Contracts;
// This behavior targets any request that is IAuditable.
// It runs first because its order is -10.
[AotCqrsBehavior(typeof(IAuditable), Order = -10)]
public class AuditBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>, IAuditable
{
/* ... implementation ... */
}
// Behaviors/ValidationBehavior.cs
using AotCqrs;
using MyWebApp.Contracts;
// This behavior targets any request that is IValidatable.
// It runs after auditing (Order 0 > -10) but before the handler.
[AotCqrsBehavior(typeof(IValidatable), Order = 0)]
public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>, IValidatable
{
/* ... implementation ... */
}
The source generator automatically finds these behaviors and registers them with the DI container for you.
Step 3: Mark Your Request
Now, simply have your request implement the marker interfaces. The pipeline will be built for it automatically at compile time.
using AotCqrs;
using MyWebApp.Contracts;
// This command will now pass through the AuditBehavior and then the ValidationBehavior
// before reaching its handler.
public record CreateUserCommand(string Email, string Password)
: IRequest, IAuditable, IValidatable;
Step 4: Register Dependencies (if any)
If your behaviors have dependencies (like IValidator<T>
for the ValidationBehavior
), register them as you normally would in Program.cs
. AddAotCqrs()
will handle the rest.
// Program.cs
// Example for FluentValidation
builder.Services.AddValidatorsFromAssemblyContaining<Program>();
// AddAotCqrs handles handler and behavior registration.
builder.Services.AddAotCqrs();
Advanced Handler Registration
For better organization in large projects, you can register handlers in a central "context" class instead of attributing every handler.
// Features/Users/UserFeature.cs
using AotCqrs;
[AotCqrsContext(
typeof(CreateUserCommandHandler),
typeof(GetUserQueryHandler),
typeof(UpdateUserCommandHandler)
)]
public static partial class UserFeature { }
The handlers listed here no longer need the [AotCqrsHandler]
attribute. The generator will find them through the context.
How It Works: The Magic of Source Generators
When you build your project, the AotCqrs source generator:
- Scans for registrations: It finds all handlers (
[AotCqrsHandler]
,[AotCqrsContext]
) and all contract-based behaviors ([AotCqrsBehavior]
). - Generates
DependencyInjection.g.cs
: This file contains theAddAotCqrs()
extension method, which explicitly registersIMediator
, every handler, and every discovered behavior. - Generates
Mediator.g.cs
: This file contains theIMediator
implementation. For each request, it generates a custom, highly-optimized pipeline by:- Finding the specific handler for the request.
- Checking which marker interfaces the request implements.
- Weaving in the corresponding behaviors in the correct order (
Order
property). - Creating a chain of calls that executes the complete pipeline.
This compile-time process results in extremely fast, AOT-safe code with zero reflection.
Contributing
Contributions are welcome! If you find a bug or have a feature request, please open an issue.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net8.0
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.