Atya.Application.Mediator
1.0.0
Prefix Reserved
dotnet add package Atya.Application.Mediator --version 1.0.0
NuGet\Install-Package Atya.Application.Mediator -Version 1.0.0
<PackageReference Include="Atya.Application.Mediator" Version="1.0.0" />
<PackageVersion Include="Atya.Application.Mediator" Version="1.0.0" />
<PackageReference Include="Atya.Application.Mediator" />
paket add Atya.Application.Mediator --version 1.0.0
#r "nuget: Atya.Application.Mediator, 1.0.0"
#:package Atya.Application.Mediator@1.0.0
#addin nuget:?package=Atya.Application.Mediator&version=1.0.0
#tool nuget:?package=Atya.Application.Mediator&version=1.0.0
Atya.Application.Mediator
Source-generated, reflection-free mediator contracts and dispatch for .NET applications.
Overview
Atya.Application.Mediator provides a small application-layer mediator for codebases that want explicit request and handler contracts without runtime assembly scanning. Handlers return Result or Result<T> so expected application outcomes stay in the Atya Results model instead of exceptions or framework-specific response objects.
The package is intentionally narrow: it owns request dispatch, compile-time handler registration, and DI wiring. Validation, ProblemDetails mapping, endpoint filters, persistence, notifications, and pipeline behavior frameworks stay outside v1.
Features
- Result-first request contracts for commands and queries.
- Inference-friendly
await mediator.Send(query)call sites. - Source-generated
services.AddAtyaMediator()registration for compile-time-discovered handlers. - Reflection-free runtime dispatch through frozen runtime-type lookup tables.
- Duplicate handler diagnostics at compile time.
- Manual
MediatorRegistrationBuilderregistration as an escape hatch.
Installation
dotnet add package Atya.Application.Mediator
Install-Package Atya.Application.Mediator
<PackageReference Include="Atya.Application.Mediator" Version="<latest-stable>" />
MediatR Migration Guide
The intended migration path keeps the call site familiar while making registration compile-time generated and results explicit:
using Atya.Application.Mediator;
using Atya.Foundation.Results;
using Microsoft.Extensions.DependencyInjection;
ServiceCollection services = new();
services.AddAtyaMediator();
using ServiceProvider provider = services.BuildServiceProvider();
IMediator mediator = provider.GetRequiredService<IMediator>();
Result<CustomerSummary> result = await mediator.Send(new GetCustomer(customerId));
Define requests and handlers with Atya contracts:
public sealed record class GetCustomer(Guid CustomerId) : IRequest<CustomerSummary>;
public sealed class GetCustomerHandler : IRequestHandler<GetCustomer, CustomerSummary>
{
public ValueTask<Result<CustomerSummary>> Handle(GetCustomer request, CancellationToken cancellationToken)
{
CustomerSummary summary = new(request.CustomerId, "Atya");
return ValueTask.FromResult(Result.Success(summary));
}
}
public sealed record class CustomerSummary(Guid CustomerId, string Name);
The source generator discovers concrete handlers during compilation and emits the handler registrations behind AddAtyaMediator(). No assembly scanning is performed at startup.
Quick Start
using Atya.Application.Mediator;
using Atya.Foundation.Results;
using Microsoft.Extensions.DependencyInjection;
ServiceCollection services = new();
services.AddAtyaMediator();
using ServiceProvider provider = services.BuildServiceProvider();
IMediator mediator = provider.GetRequiredService<IMediator>();
Result<string> result = await mediator.Send(new CreateGreeting("Atya"));
Console.WriteLine(result.IsSuccess ? result.Value : result.Error.Message);
public sealed record class CreateGreeting(string Name) : IRequest<string>;
public sealed class CreateGreetingHandler : IRequestHandler<CreateGreeting, string>
{
public ValueTask<Result<string>> Handle(CreateGreeting request, CancellationToken cancellationToken) =>
ValueTask.FromResult(Result.Success($"Hello, {request.Name}."));
}
Contracts
Use IRequest for commands that return only success or failure:
public sealed record class ArchiveOrder(Guid OrderId) : IRequest;
public sealed class ArchiveOrderHandler : IRequestHandler<ArchiveOrder>
{
public ValueTask<Result> Handle(ArchiveOrder request, CancellationToken cancellationToken)
{
return ValueTask.FromResult(Result.Success());
}
}
Use IRequest<TResponse> for queries or commands with a value:
public sealed record class GetOrder(Guid OrderId) : IRequest<OrderSummary>;
public sealed class GetOrderHandler : IRequestHandler<GetOrder, OrderSummary>
{
public ValueTask<Result<OrderSummary>> Handle(GetOrder request, CancellationToken cancellationToken)
{
return ValueTask.FromResult(Result.Success(new OrderSummary(request.OrderId)));
}
}
public sealed record class OrderSummary(Guid OrderId);
Sending Requests
Use inference-friendly overloads for normal call sites:
Result archive = await mediator.Send(new ArchiveOrder(orderId));
Result<OrderSummary> order = await mediator.Send(new GetOrder(orderId));
The two-generic overloads remain available as explicit fast paths:
Result archive = await mediator.Send<ArchiveOrder>(new ArchiveOrder(orderId));
Result<OrderSummary> order = await mediator.Send<GetOrder, OrderSummary>(new GetOrder(orderId));
Missing handlers are configuration errors. IMediator.Send throws InvalidOperationException naming the request type and the registration fix when no handler is registered. Missing and duplicate registrations are treated consistently as configuration failures, not Result failures.
Registration
In normal applications, call the generated one-line registration:
services.AddAtyaMediator();
The package includes a Roslyn source generator packaged as an analyzer asset. It discovers concrete IRequestHandler<TRequest> and IRequestHandler<TRequest,TResponse> implementations at compile time and emits deterministic registration code. If more than one handler targets the same request/response shape, the generator reports ATYAMEDIATOR001 as a compile-time error.
Manual registration remains available for tests and advanced composition:
services.AddAtyaMediator(builder =>
{
builder.AddRequestHandler<ArchiveOrder, ArchiveOrderHandler>();
builder.AddRequestHandler<GetOrder, OrderSummary, GetOrderHandler>();
});
Manual registration uses the same runtime dispatcher bridge as generated registration. The runtime does not scan assemblies.
Deliberate Omissions
v1 deliberately does not include pipeline behaviors, notifications, streaming requests, or handler lifetime policies beyond DI registration. Pipeline behaviors are planned as an additive 1.1.0 feature after the v1 dispatch and source-generation surface is stable.
Why These Dependencies
Atya.Foundation.Guardsvalidates public entry points and configuration arguments.Atya.Foundation.Resultsis the package's expected-outcome model.Microsoft.Extensions.DependencyInjection.Abstractionsprovides the DI registration surface without forcing a concrete container.
The Roslyn source generator is packaged as an analyzer asset and does not add runtime dependencies to consuming applications.
Compatibility
Targets net10.0.
Testing
dotnet test
Benchmarks
Benchmarks live in benchmarks/Mediator.Benchmarks.
dotnet run --project benchmarks/Mediator.Benchmarks/Mediator.Benchmarks.csproj -c Release -- --list flat
License
Released under the MIT license. See LICENSE.
Links
| 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
- Atya.Foundation.Guards (>= 1.0.2)
- Atya.Foundation.Results (>= 1.1.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 122 | 7/10/2026 |