Request / Response
Send requests through the mediator and get type-safe responses back from the matching handler.
Pipelink is a modern, performance-focused .NET library that lets you implement the CQRS pattern in your application by routing requests, publishing notifications, and delegating work to handlers and pipeline behaviors.
public class UserController : ControllerBase
{
private readonly Pipelink _pipelink;
public UserController(Pipelink pipelink)
=> _pipelink = pipelink;
[HttpGet("{id}")]
public async Task<ActionResult<UserDto>> GetUser(int id)
{
var user = await _pipelink.Send(new GetUserQuery(id));
return Ok(user);
}
}
Pipelink gives you the essential building blocks to implement a CQRS architecture through a clean, readable API.
Send requests through the mediator and get type-safe responses back from the matching handler.
Publish a single event to multiple handlers and keep your business logic loosely coupled.
Layer in cross-cutting concerns like logging, validation, and caching around your requests.
Built-in streaming support for efficiently handling large datasets.
Built-in compression support to optimize data transfer.
Seamless, natural integration with Microsoft.Extensions.DependencyInjection.
Your handlers and behaviors are automatically registered via assembly scanning.
Measured on .NET 8.0, Release configuration, on an Apple M3 processor using BenchmarkDotNet v0.14.0.
| Method | Mean | Error | StdDev | Allocated |
|---|---|---|---|---|
| Pipelink_Send | 39.10 ns | 0.179 ns | 0.340 ns | 280 B |
* Results may vary depending on hardware and environment.
Sub-microsecond mediator dispatch on average
Minimal memory allocation per request
Low standard deviation for consistent, predictable performance
Register Pipelink in your project, define your request and response, write your handler, and start using it.
// In Program.cs or Startup.cs
services.AddPipelink(cfg =>
{
cfg.RegisterServicesFromAssemblyContaining<Startup>();
});
// Request
public record GetUserQuery(int UserId) : IRequest<UserDto>;
// Response
public record UserDto(int Id, string Name, string Email);
public class GetUserQueryHandler : IRequestHandler<GetUserQuery, UserDto>
{
public async Task<UserDto> Handle(GetUserQuery request, CancellationToken cancellationToken)
{
// Your implementation here
return new UserDto(request.UserId, "John Doe", "john@example.com");
}
}
public class UserController : ControllerBase
{
private readonly Pipelink _pipelink;
public UserController(Pipelink pipelink)
=> _pipelink = pipelink;
[HttpGet("{id}")]
public async Task<ActionResult<UserDto>> GetUser(int id)
{
var user = await _pipelink.Send(new GetUserQuery(id));
return Ok(user);
}
}
Ready for the advanced scenarios you'll need as your application grows.
Publish a single event to multiple independent handlers with no dependencies between them.
public record UserCreatedNotification(int UserId, string Email) : INotification;
public class SendWelcomeEmailHandler
: INotificationHandler<UserCreatedNotification>
{
public async Task Handle(UserCreatedNotification n, CancellationToken ct)
{
// Send a welcome email
}
}
await _pipelink.Publish(new UserCreatedNotification(userId, email));
Wrap cross-cutting concerns like logging around your requests, before and after handling.
public class LoggingBehavior<TRequest, TResponse>
: IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
public async Task<TResponse> Handle(
TRequest request, CancellationToken ct,
RequestHandlerDelegate<TResponse> next)
{
_logger.LogInformation("Handling {Request}", request.GetType().Name);
var response = await next();
_logger.LogInformation("Handled {Request}", request.GetType().Name);
return response;
}
}
A focused toolkit built around a few core ideas:
Pipelink is distributed via NuGet and works with any project targeting .NET 8.0.
dotnet add package Pipelink