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

NuGet

CI Code Coverage .NET 9 License: MIT

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 and IQuery interfaces.
  • Implement request handlers with ICommandHandler or IQueryHandler.
  • Plug in pipeline behaviours with PreHandle and PostHandle 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:

  • Toarnbeike.Results

Usage

  1. Register messaging:
  builder.Services.AddRequestMessaging(options =>
        options.FromAssemblyContaining<GetUserQueryHandler>());
  1. 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());
    }
}
  1. 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 a RequestExceedsExpectedDurationNotification if the configured threshold (default 5 seconds) is exceeded.
  • FluentValidationPipelineBehaviour: Validates incoming requests using registered IValidator<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:

  1. Notificaitons are fist stored in an INotificationStore.
  2. The INotificationPublisher retrieves and dispatches them.
  3. Each notificiaotn is delivered to all matching handlers.

Flow:

  1. 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.

  2. Define a notification by inheriting NotificationBase

    public record CustomerCreatedNotification(int CustomerId) : NotificationBase("Sender");
    
  3. 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;
        }
    }
    
  4. 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 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. 
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 223 8/28/2025