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
                    
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="Atya.Application.Mediator" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Atya.Application.Mediator" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Atya.Application.Mediator" />
                    
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 Atya.Application.Mediator --version 1.0.0
                    
#r "nuget: Atya.Application.Mediator, 1.0.0"
                    
#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 Atya.Application.Mediator@1.0.0
                    
#: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=Atya.Application.Mediator&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Atya.Application.Mediator&version=1.0.0
                    
Install as a Cake Tool

Atya.Application.Mediator

Source-generated, reflection-free mediator contracts and dispatch for .NET applications.

NuGet Version Downloads .NET 10.0 License: MIT

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 MediatorRegistrationBuilder registration 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.Guards validates public entry points and configuration arguments.
  • Atya.Foundation.Results is the package's expected-outcome model.
  • Microsoft.Extensions.DependencyInjection.Abstractions provides 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.

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

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