Mediary 1.0.0
dotnet add package Mediary --version 1.0.0
NuGet\Install-Package Mediary -Version 1.0.0
<PackageReference Include="Mediary" Version="1.0.0" />
<PackageVersion Include="Mediary" Version="1.0.0" />
<PackageReference Include="Mediary" />
paket add Mediary --version 1.0.0
#r "nuget: Mediary, 1.0.0"
#:package Mediary@1.0.0
#addin nuget:?package=Mediary&version=1.0.0
#tool nuget:?package=Mediary&version=1.0.0
Mediary — Lightweight Request Dispatcher for .NET
Mediary is a minimal, open-source library for .NET that implements the Request/Handler (Mediator) pattern with pipeline support — inspired by MediatR, but built from scratch with no third-party dependencies.
Clean request handling, extensible pipeline behaviors, and a DI-friendly architecture.
🚨 Why Mediary?
Mediary is a lightweight request dispatcher for .NET designed to offer a clean, extensible, and dependency-free alternative to more complex mediators.
It focuses on performance, clarity, and developer control, while maintaining compatibility with the .NET dependency injection ecosystem.
✅ Key benefits
- ⚡ Lightweight and fast — no unnecessary overhead or runtime reflection
- 🧩 Extensible pipeline behaviors — clean middleware-style request handling
- 🧼 Minimalist design — no third-party dependencies, no magic
- 🧪 Test-friendly — everything is composable and DI-compatible
- 📦 NuGet-ready — simple to install and integrate
🧭 Table of Contents
- Installation
- Features
- Quick Start
- Dependency Injection
- Pipeline Behaviors
- Aliases
- Result Types
- Request Metadata
📦 Installation
You can install Mediary via NuGet:
dotnet add package Mediary
Or via the NuGet UI in Visual Studio by searching for Mediary.
📦 Available at: https://www.nuget.org/packages/Mediary
🚀 Features
- ✅ Async request handling via
IRequest<TResponse> - ✅ Handler support via
IRequestHandler<TResponse, TRequest> - ✅ Semantic result types:
Unit,Success,Created,Updated,Deleted - ✅ Dispatcher support via
IRequestDispatcher - ✅ Middleware support via
IRequestPipelineBehavior<TResponse, TRequest> - ✅ Generic and specific pipeline registration
- ✅ Optional
[RequestInfo]metadata for descriptive logging and tooling - ✅ Aliases for semantic intent:
ICommand<TResponse>,IQuery<TResponse>
🔧 Quick Start
1. Define a Request
public class GetAllPlansQuery : IQuery<List<PlanDto>> { }
2. Create a Handler
public class GetAllPlansHandler : IRequestHandler<List<PlanDto>, GetAllPlansQuery>
{
public Task<List<PlanDto>> HandleAsync(GetAllPlansQuery request)
{
// Your logic here
}
}
3. Inject the Dispatcher
public class PlanService
{
private readonly IRequestDispatcher _dispatcher;
public PlanService(IRequestDispatcher dispatcher) =>
_dispatcher = dispatcher;
public Task<List<PlanDto>> GetPlansAsync() =>
_dispatcher.DispatchAsync<List<PlanDto>, GetAllPlansQuery>(new GetAllPlansQuery());
}
🛠 Dependency Injection
Mediary supports flexible registration depending on your needs:
✅ Option 1 — Auto-registration (Recommended)
services.AddMediary()
.AddRequestHandlersFromAssembly(typeof(Program).Assembly)
.AddPipelineBehaviorsFromAssembly(typeof(Program).Assembly);
This will:
- Register the
IRequestDispatcher - Scan and register all
IRequestHandler<,> - Scan and register all
IRequestPipelineBehavior<,>
🔧 Option 2 — Semi-manual (Builder API)
If you prefer full control but want to use the builder pattern:
services.AddMediary()
.AddRequestHandler<List<PlanDto>, GetAllPlansQuery, GetAllPlansHandler>()
.AddPipelineBehaviors<List<PlanDto>, GetAllPlansQuery, LoggingBehavior<List<PlanDto>, GetAllPlansQuery>>();
You can also override the dispatcher:
services.AddMediary<CustomDispatcher>();
🧩 Option 3 — Fully manual
If you want zero coupling to Mediary's builder or extension methods, you can register everything manually:
services.AddScoped<IRequestDispatcher, RequestDispatcher>();
services.AddScoped<IRequestHandler<List<PlanDto>, GetAllPlansQuery>, GetAllPlansHandler>();
services.AddScoped<IRequestPipelineBehavior<List<PlanDto>, GetAllPlansQuery>, LoggingBehavior<List<PlanDto>, GetAllPlansQuery>>();
This gives you maximum flexibility and full control over dependency injection.
🔍 Pipeline Behaviors
Pipeline behaviors wrap request handling and allow you to implement cross-cutting concerns like logging, validation, caching, etc.
Built-in Behaviors
| Behavior | Description |
|---|---|
LoggingBehavior<TResponse, TRequest> |
Logs the start and end of a request using ILogger. |
PerformanceBehavior<TResponse, TRequest> |
Measures and logs execution time of each request. |
Both behaviors support the [RequestInfo] attribute for enriched log output.
Custom Behaviors
Implement IRequestPipelineBehavior<TResponse, TRequest> to create your own:
public class ValidationBehavior<TResponse, TRequest> : IRequestPipelineBehavior<TResponse, TRequest>
where TRequest : IRequest<TResponse>
{
public async Task<TResponse> HandleAsync(TRequest request, Func<Task<TResponse>> next)
{
// Logic before the handler
var response = await next();
// Logic after the handler
return response;
}
}
Global Registration
You can register behaviors globally for all requests:
Using the MediaryBuilder
services.AddMediary()
.AddOpenPipelineBehaviors(typeof(LoggingBehavior<,>))
.AddOpenPipelineBehaviors(typeof(PerformanceBehavior<,>));
Manual registration
services.AddScoped(typeof(IRequestPipelineBehavior<,>), typeof(LoggingBehavior<,>));
services.AddScoped(typeof(IRequestPipelineBehavior<,>), typeof(PerformanceBehavior<,>));
🪪 Aliases
For semantic clarity, Mediary exposes aliases for intent-based request types:
| Alias | Inherits | Purpose |
|---|---|---|
IQuery<TResponse> |
IRequest<TResponse> |
Read-only request |
ICommand<TResponse> |
IRequest<TResponse> |
Write command |
Aliases are optional and meant to improve readability:
// Represents: GET /users/{id}
public class GetUserByIdQuery : IQuery<UserDto> { }
// Represents: POST /users
public class CreateUserCommand : ICommand<UserDto> { }
✅ Result Types
When you don't need to return data from a command, you can use semantic result types to indicate intent:
| Type | Purpose |
|---|---|
Unit |
Used when no response is needed (replaces void) |
Success |
Indicates a generic successful result |
Created |
Indicates a resource was created |
Updated |
Indicates a resource was updated |
Deleted |
Indicates a resource was deleted |
These types are lightweight readonly structs defined in Mediary.Core.Results.
Example: Command with no output
public class ClearCacheCommand : IRequest<Unit> { }
public class ClearCacheHandler : IRequestHandler<Unit, ClearCacheCommand>
{
public Task<Unit> HandleAsync(ClearCacheCommand request) =>
Task.FromResult(Unit.Value);
}
Example: Command with semantic return
public class CreateUserCommand : IRequest<Created> { }
public class CreateUserHandler : IRequestHandler<Created, CreateUserCommand>
{
public Task<Created> HandleAsync(CreateUserCommand request) =>
Task.FromResult(Created.Value);
}
Using the Result helper
The static Result class provides convenient access to all result singletons:
Result.Unit // equivalent to Unit.Value
Result.Success // equivalent to Success.Value
Result.Created // equivalent to Created.Value
Result.Updated // equivalent to Updated.Value
Result.Deleted // equivalent to Deleted.Value
🔖 Request Metadata
You can optionally annotate your request types with descriptive metadata using the [RequestInfo] attribute:
[RequestInfo("Creates a new user", "Command", "Users")]
public class CreateUserCommand : ICommand<Guid> { }
This helps document the purpose of the request and can be consumed by logging, debugging, or inspection tools.
Both built-in behaviors (LoggingBehavior and PerformanceBehavior) will use this description if available.
Extension Methods
The metadata is accessible at runtime via extension methods on IRequest<TResponse>:
request.GetDescription() // Returns the description string, or null
request.GetTags() // Returns the tags array
request.GetInfo() // Returns the full RequestInfoAttribute, or null
📄 License
MIT License — Free for personal and commercial use.
🙌 Contributing
Open to feedback, ideas, PRs and improvements — feel free to fork and collaborate.
👨💻 Author
Built with ❤️ by Facundo Juarez — GitHub
| 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.