Dispatchly 1.0.5

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

Dispatchly

A lightweight, delegate-based CQRS framework for .NET that provides command/query separation with pipeline behaviors and notification support.

Features

  • Delegate-based: Uses delegates instead of reflection for better performance
  • Thread-safe: Built with ConcurrentDictionary and ConcurrentBag for thread safety
  • Pipeline Behaviors: Support for cross-cutting concerns like logging, validation, caching
  • Notifications: Publish/subscribe pattern for domain events
  • Cancellation Support: Full CancellationToken support throughout
  • Production Ready: Comprehensive error handling, validation, and disposal patterns
  • XML Documentation: Complete API documentation
  • Nullable Reference Types: Full null safety support

Quick Start

Installation

dotnet add package Dispatchly

Basic Usage

using Dispatchly;

// Create dispatcher
var dispatcher = new Dispatcher();

// Register command handler
dispatcher.RegisterCommandHandler<CreateUserCommand>(async (command, cancellationToken) =>
{
    // Handle the command
    await CreateUserAsync(command, cancellationToken);
});

// Register query handler
dispatcher.RegisterQueryHandler<GetUserQuery, User>(async (query, cancellationToken) =>
{
    // Handle the query
    return await GetUserAsync(query.UserId, cancellationToken);
});

// Send a command
await dispatcher.Send(new CreateUserCommand { Name = "John", Email = "john@example.com" });

// Execute a query
var user = await dispatcher.Query(new GetUserQuery { UserId = 123 });

Advanced Usage

Pipeline Behaviors

// Logging behavior
dispatcher.UseCommandBehavior<CreateUserCommand>(async (command, next, cancellationToken) =>
{
    Console.WriteLine($"Executing command: {command.GetType().Name}");
    try
    {
        await next();
        Console.WriteLine("Command executed successfully");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Command failed: {ex.Message}");
        throw;
    }
});

// Validation behavior
dispatcher.UseQueryBehavior<GetUserQuery, User>(async (query, next, cancellationToken) =>
{
    if (query.UserId <= 0)
        throw new ArgumentException("User ID must be positive");
    
    return await next();
});

Notifications

// Register notification handlers
dispatcher.RegisterNotificationHandler<UserCreatedEvent>(async (notification, cancellationToken) =>
{
    await SendWelcomeEmailAsync(notification.UserId, cancellationToken);
});

dispatcher.RegisterNotificationHandler<UserCreatedEvent>(async (notification, cancellationToken) =>
{
    await UpdateAuditLogAsync(notification, cancellationToken);
});

// Publish notification (multiple handlers will execute)
await dispatcher.Publish(new UserCreatedEvent { UserId = 123, Name = "John" });

Error Handling

try
{
    await dispatcher.Send(new CreateUserCommand { Name = "John" });
}
catch (HandlerNotFoundException ex)
{
    Console.WriteLine($"No handler found for {ex.MessageType.Name}");
}
catch (ArgumentNullException ex)
{
    Console.WriteLine($"Null argument: {ex.ParamName}");
}

API Reference

Core Interfaces

  • ICommand - Marker interface for commands
  • IQuery<TResult> - Marker interface for queries
  • INotification - Marker interface for notifications

Delegates

  • CommandHandlerDelegate<TCommand> - Handles commands
  • QueryHandlerDelegate<TQuery, TResult> - Handles queries
  • CommandPipelineDelegate<TCommand> - Command pipeline behavior
  • QueryPipelineDelegate<TQuery, TResult> - Query pipeline behavior
  • NotificationHandlerDelegate<TNotification> - Handles notifications

Dispatcher Methods

  • RegisterCommandHandler<TCommand>() - Register command handler
  • RegisterQueryHandler<TQuery, TResult>() - Register query handler
  • RegisterNotificationHandler<TNotification>() - Register notification handler
  • UseCommandBehavior<TCommand>() - Add command pipeline behavior
  • UseQueryBehavior<TQuery, TResult>() - Add query pipeline behavior
  • Send<TCommand>() - Send command
  • Query<TQuery, TResult>() - Execute query
  • Publish<TNotification>() - Publish notification
  • HasCommandHandler<TCommand>() - Check if command handler exists
  • HasQueryHandler<TQuery>() - Check if query handler exists
  • HasNotificationHandlers<TNotification>() - Check if notification handlers exist

Properties

  • CommandHandlerCount - Number of registered command handlers
  • QueryHandlerCount - Number of registered query handlers
  • NotificationHandlerCount - Number of registered notification handlers
  • CommandBehaviorCount - Number of registered command behaviors
  • QueryBehaviorCount - Number of registered query behaviors

Best Practices

1. Use Dependency Injection

services.AddSingleton<Dispatcher>();

2. Implement Proper Error Handling

public async Task<Result<User>> CreateUserAsync(CreateUserCommand command)
{
    try
    {
        await _dispatcher.Send(command);
        return Result<User>.Success();
    }
    catch (HandlerNotFoundException)
    {
        return Result<User>.Failure("Handler not configured");
    }
    catch (Exception ex)
    {
        return Result<User>.Failure(ex.Message);
    }
}

3. Use Cancellation Tokens

public async Task<User> GetUserAsync(int userId, CancellationToken cancellationToken = default)
{
    return await _dispatcher.Query(new GetUserQuery { UserId = userId }, cancellationToken);
}

4. Implement Pipeline Behaviors for Cross-cutting Concerns

// Logging
dispatcher.UseCommandBehavior<CreateUserCommand>(async (command, next, cancellationToken) =>
{
    _logger.LogInformation("Executing {CommandType}", command.GetType().Name);
    await next();
    _logger.LogInformation("Executed {CommandType}", command.GetType().Name);
});

// Validation
dispatcher.UseCommandBehavior<CreateUserCommand>(async (command, next, cancellationToken) =>
{
    var validator = new CreateUserCommandValidator();
    var result = await validator.ValidateAsync(command, cancellationToken);
    
    if (!result.IsValid)
        throw new ValidationException(result.Errors);
    
    await next();
});

5. Proper Disposal

public class MyService : IDisposable
{
    private readonly Dispatcher _dispatcher;
    
    public MyService(Dispatcher dispatcher)
    {
        _dispatcher = dispatcher;
    }
    
    public void Dispose()
    {
        _dispatcher?.Dispose();
    }
}

Performance Considerations

  • Delegate-based: No reflection overhead
  • Thread-safe: Concurrent collections for high-performance scenarios
  • Memory efficient: Proper disposal patterns
  • Async/await: Full async support throughout

License

MIT License - see LICENSE file for details.

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.
  • net9.0

    • No dependencies.

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.5 437 7/24/2025
1.0.2 131 7/14/2025
1.0.1 92 7/11/2025
1.0.0 89 7/11/2025