Toarnbeike.Results.Messaging
1.0.0
dotnet add package Toarnbeike.Results.Messaging --version 1.0.0
NuGet\Install-Package Toarnbeike.Results.Messaging -Version 1.0.0
<PackageReference Include="Toarnbeike.Results.Messaging" Version="1.0.0" />
<PackageVersion Include="Toarnbeike.Results.Messaging" Version="1.0.0" />
<PackageReference Include="Toarnbeike.Results.Messaging" />
paket add Toarnbeike.Results.Messaging --version 1.0.0
#r "nuget: Toarnbeike.Results.Messaging, 1.0.0"
#:package Toarnbeike.Results.Messaging@1.0.0
#addin nuget:?package=Toarnbeike.Results.Messaging&version=1.0.0
#tool nuget:?package=Toarnbeike.Results.Messaging&version=1.0.0
Toarnbeike.Results.Messaging
Lightweight CQRS request/response messaging built on top of Toarnbeike.Results.
Define commands and queries, dispatch them dynamically, and process results without exceptions.
Features
- Define requests via
ICommand
andIQuery
interfaces. - Implement request handlers with
ICommandHandler
orIQueryHandler
. - Plug in pipeline behaviours with
PreHandle
andPostHandle
hooks. - Built-in pagination support via
IPaginatedQuery
. - Automatic discovery and registration of handlers and pipelines in DI.
- Result-first design: rich error handling without exceptions.
- Fire-and-forget notifications with support for multiple handlers and outbox-style persistence.
Getting started
dotnet add package Toarnbeike.Results.Messaging
This package targets .NET 9+
and depends on:
Usage
- Register messaging:
builder.Services.AddRequestMessaging(options =>
options.FromAssemblyContaining<GetUserQueryHandler>());
- Define request & handler:
public sealed record GetUserQuery(Guid Id) : IQuery<UserDto>;
internal sealed class GetUserQueryHandler(IUserRepository userRepository)
: IQueryHandler<GetUserQuery, UserDto>
{
public async Task<Result<UserDto>> HandleAsync(GetUserQuery query, CancellationToken ct = default)
{
return await Result.Success(query.Id)
.Map(id => new UserId(id))
.MapAsync(userRepository.GetByIdAsync, ct)
.Map(user => user.ToDto());
}
}
- Dispatch from your service:
public class FrontEndUserService(IRequestDispatcher dispatch)
{
public async Task<UserDto> GetUserById(Guid id)
{
var query = new GetUserQuery(id);
return await dispatch.HandleAsync(query);
}
}
Pipeline behaviour
Pipeline behaviours wrap request handling with cross-cutting logic. Typical examples include validation, logging, or caching.
Build in pipelines:
PerformanceLoggingPipelineBehaviour
: Logs incoming requests, measures execution time, and sends aRequestExceedsExpectedDurationNotification
if the configured threshold (default 5 seconds) is exceeded.FluentValidationPipelineBehaviour
: Validates incoming requests using registeredIValidator<TRequest>
instances. Short-circuits execution if the request is invalid.
Custom pipeline example:
public sealed class LoggingBehaviour<TRequest, TResponse>(
ILogger<LoggingBehaviour<TRequest, TResponse>> logger) : IPipelineBehaviour<TRequest, TResponse>
where TRequest : class, IRequest<TResponse> where TResponse : IResult
{
private string _requestName = "";
private string _responseName = "";
/// <inheritdoc />
public Task<Result> PreHandleAsync(TRequest request, CancellationToken cancellationToken = default)
{
_requestName = request.GetType().PrettyName();
_responseName = typeof(TResponse).PrettyName();
logger.LogInformation("Handling {RequestType} => {ResponseType}", _requestName, _responseName);
return Result.SuccessTask();
}
/// <inheritdoc />
public Task<Result> PostHandleAsync(TRequest request, TResponse response, CancellationToken cancellationToken = default)
{
logger.LogInformation("Handled {RequestType} => {ResponseType}. Result was {Result}",
_requestName,
_responseName,
response.IsSuccess ? "Success" : "Failure");
return Result.SuccessTask();
}
}
Registering behaviours:
builder.Services.AddRequestMessaging(o =>
o.AddValidationBehaviour();
o.AddPerformanceLoggingBehavior(configure => configure.MaxExpectedDuration = Timespan.FromSeconds(1));
o.AddPipelineBehaviour(typeof(LoggingBehaviour<,>)));
The order in which the pipelines are registered is the order in which they are executed.
Notifications
In addition to commands and queries, this package supports notifications. Notifications are fire-and-forget messages that can be processed by multiple handlers at once.
They follow an outbox-style flow:
- Notificaitons are fist stored in an
INotificationStore
. - The
INotificationPublisher
retrieves and dispatches them. - Each notificiaotn is delivered to all matching handlers.
Flow:
Register notification messaging in your DI setup:
services.AddNotificationMessaging(options => { // Assemblies containing notification handlers options.FromAssemblyContaining<MyNotificationHandler>(); // Optionally: register a custom notification store (e.g. EF Core) options.UseCustomNotificationStore<MyEfCoreNotificationStore>(); });
If no custom store is provided, an in-memory store is used by default.
Define a notification by inheriting NotificationBase
public record CustomerCreatedNotification(int CustomerId) : NotificationBase("Sender");
Write a handler for the notification
public sealed class SendWelcomeEmailHandler : INotificationHandler<CustomerCreatedNotification> { public Task HandleAsync(CustomerCreatedNotification notification, CancellationToken cancellationToken) { // Send welcome email return Task.CompletedTask; } }
Publish the notification
public sealed class CustomerService(INotificationStore notificationStore) { public async Task AddCustomerAsync() { // ... register user ... var customerId = GetNewId(); await _store.AddAsync(new UserRegisteredNotification(customerId)); } }
Notifications are persisted first and later dispatched asynchronously by the publisher.
Summary
- Commands/Queries → request/response with rich result handling.
- Pipelines → plug in cross-cutting behaviours (validation, logging, caching).
- Notifications → fire-and-forget messages; processed reliably by multiple handlers.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. 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. |
-
net9.0
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.8)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.8)
- Toarnbeike.Results.FluentValidation (>= 1.0.0)
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 | 223 | 8/28/2025 |