SliceDispatch 1.2.0

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

SliceDispatch

╔═══════════════════════════════════════════════════════════════════════════╗
║                                                                           ║
║   ███████╗██╗     ██╗ ██████╗███████╗                                    ║
║   ██╔════╝██║     ██║██╔════╝██╔════╝                                    ║
║   ███████╗██║     ██║██║     █████╗                                      ║
║   ╚════██║██║     ██║██║     ██╔══╝                                      ║
║   ███████║███████╗██║╚██████╗███████╗                                    ║
║   ╚══════╝╚══════╝╚═╝ ╚═════╝╚══════╝                                    ║
║                                                                           ║
║   ██████╗ ██╗███████╗██████╗  █████╗ ████████╗ ██████╗██╗  ██╗          ║
║   ██╔══██╗██║██╔════╝██╔══██╗██╔══██╗╚══██╔══╝██╔════╝██║  ██║          ║
║   ██║  ██║██║███████╗██████╔╝███████║   ██║   ██║     ███████║          ║
║   ██║  ██║██║╚════██║██╔═══╝ ██╔══██║   ██║   ██║     ██╔══██║          ║
║   ██████╔╝██║███████║██║     ██║  ██║   ██║   ╚██████╗██║  ██║          ║
║   ╚═════╝ ╚═╝╚══════╝╚═╝     ╚═╝  ╚═╝   ╚═╝    ╚═════╝╚═╝  ╚═╝          ║
║                                                                           ║
║          ▓▓▓▓▓ REQUEST DISPATCHING SYSTEM ONLINE ▓▓▓▓▓                   ║
║                                                                           ║
║          >> Clean Architecture • CQRS • Vertical Slice                   ║
║          >> Developer: Adam Watson                                       ║
║          >> STATUS: [■■■■■■■■■■] 100% OPERATIONAL                        ║
║                                                                           ║
╚═══════════════════════════════════════════════════════════════════════════╝

Lightweight request dispatching for CQRS and Vertical Slice Architecture.

SliceDispatch is a lightweight .NET library for request/handler orchestration inspired by mediator-style patterns. It is designed for developers who want clean request handling without unnecessary complexity.

Features

  • Request / response dispatching
  • Void request dispatching
  • Notification publishing (publish/subscribe with multiple handlers)
  • Pipeline behaviors for requests and notifications
  • Automatic handler and closed-behavior registration via assembly scanning
  • Open-generic behavior registration for cross-cutting concerns
  • Reflection-cached dispatch via compiled delegates (warm path has zero reflection overhead)
  • Custom missing-handler exceptions
  • Built for CQRS and Vertical Slice Architecture

Installation

dotnet add package SliceDispatch

Basic Example

using Microsoft.Extensions.DependencyInjection;
using SliceDispatch;

var services = new ServiceCollection();

services.AddSliceDispatch(typeof(PingRequestHandler).Assembly);

var provider = services.BuildServiceProvider();

var sender = provider.GetRequiredService<ISender>();

var result = await sender.Send(new PingRequest());

Console.WriteLine(result); // Pong

public sealed class PingRequest : IRequest<string>
{
}

public sealed class PingRequestHandler : IRequestHandler<PingRequest, string>
{
    public Task<string> Handle(
        PingRequest request,
        CancellationToken cancellationToken)
    {
        return Task.FromResult("Pong");
    }
}

Notifications (Publish/Subscribe)

Notifications enable the publish/subscribe pattern where a single event can trigger multiple independent handlers.

Basic Notification Example

using Microsoft.Extensions.DependencyInjection;
using SliceDispatch;

var services = new ServiceCollection();
services.AddSliceDispatch(typeof(OrderPlacedNotification).Assembly);

var provider = services.BuildServiceProvider();
var sender = provider.GetRequiredService<ISender>();

// Publish notification - all handlers execute
await sender.Publish(new OrderPlacedNotification 
{ 
    OrderId = "12345", 
    Amount = 99.99m 
});

// Define notification
public sealed class OrderPlacedNotification : INotification
{
    public string OrderId { get; init; } = string.Empty;
    public decimal Amount { get; init; }
}

// Multiple handlers for the same notification
public sealed class SendEmailHandler : INotificationHandler<OrderPlacedNotification>
{
    public async Task Handle(OrderPlacedNotification notification, CancellationToken cancellationToken)
    {
        // Send confirmation email
        Console.WriteLine($"Email sent for order {notification.OrderId}");
        await Task.CompletedTask;
    }
}

public sealed class UpdateInventoryHandler : INotificationHandler<OrderPlacedNotification>
{
    public async Task Handle(OrderPlacedNotification notification, CancellationToken cancellationToken)
    {
        // Update inventory
        Console.WriteLine($"Inventory updated for order {notification.OrderId}");
        await Task.CompletedTask;
    }
}

public sealed class LogOrderHandler : INotificationHandler<OrderPlacedNotification>
{
    public async Task Handle(OrderPlacedNotification notification, CancellationToken cancellationToken)
    {
        // Log order details
        Console.WriteLine($"Order logged: {notification.OrderId} - ${notification.Amount}");
        await Task.CompletedTask;
    }
}

Key Points:

  • Handlers execute sequentially in registration order
  • If no handlers are registered, Publish() returns silently (no exception)
  • All handlers receive the same notification instance
  • Pipeline behaviors can wrap each handler individually

Pipeline Behaviors

Pipeline behaviors wrap handler execution and are ideal for cross-cutting concerns like logging, validation, and timing.

Open-generic behavior (applies to all requests of matching shape)

Register manually to keep ordering explicit:

// Register in DI — order of registration = outer-to-inner execution order
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));

public sealed class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    public async Task<TResponse> Handle(
        TRequest request,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken cancellationToken)
    {
        Console.WriteLine($"Handling {typeof(TRequest).Name}");
        var response = await next();
        Console.WriteLine($"Handled {typeof(TRequest).Name}");
        return response;
    }
}

Closed behavior (applies to one specific request type)

Closed behaviors are discovered automatically by AddSliceDispatch assembly scanning — no manual registration needed:

// Automatically registered when the assembly is scanned
public sealed class AuditBehavior : IPipelineBehavior<PlaceOrderRequest, OrderResult>
{
    public async Task<OrderResult> Handle(
        PlaceOrderRequest request,
        RequestHandlerDelegate<OrderResult> next,
        CancellationToken cancellationToken)
    {
        var result = await next();
        // audit logic here
        return result;
    }
}

Void request behaviors

Both open-generic and closed behaviors are supported for void requests (IRequest) using IPipelineBehavior<TRequest>:

public sealed class LoggingBehavior<TRequest> : IPipelineBehavior<TRequest>
    where TRequest : IRequest
{
    public async Task Handle(
        TRequest request,
        RequestHandlerDelegate next,
        CancellationToken cancellationToken)
    {
        Console.WriteLine($"Handling {typeof(TRequest).Name}");
        await next();
        Console.WriteLine($"Handled {typeof(TRequest).Name}");
    }
}

Notification Pipeline Behaviors

Notifications support pipeline behaviors using INotificationPipelineBehavior<TNotification>. Each handler is wrapped individually by the pipeline.

// Open-generic notification behavior (applies to all notifications)
public sealed class LoggingBehavior<TNotification> : INotificationPipelineBehavior<TNotification>
    where TNotification : INotification
{
    public async Task Handle(
        TNotification notification,
        RequestHandlerDelegate next,
        CancellationToken cancellationToken)
    {
        Console.WriteLine($"Handling notification {typeof(TNotification).Name}");
        await next();
        Console.WriteLine($"Completed notification {typeof(TNotification).Name}");
    }
}

// Register manually
builder.Services.AddTransient(typeof(INotificationPipelineBehavior<>), typeof(LoggingBehavior<>));

Closed notification behaviors are automatically discovered via assembly scanning:

// This will be automatically registered
public sealed class OrderAuditBehavior : INotificationPipelineBehavior<OrderPlacedNotification>
{
    public async Task Handle(
        OrderPlacedNotification notification,
        RequestHandlerDelegate next,
        CancellationToken cancellationToken)
    {
        // Audit before handlers
        await next(); // Each handler wrapped individually
        // Audit after handlers
    }
}

ASP.NET Core Example

var builder = WebApplication.CreateBuilder(args);

// Scans assembly for handlers and closed pipeline behaviors
builder.Services.AddSliceDispatch(typeof(PingRequestHandler).Assembly);

// Register open-generic behaviors manually to control ordering
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));

var app = builder.Build();

app.MapGet("/ping", async (ISender sender) =>
{
    return await sender.Send(new PingRequest());
});

app.Run();

Migrating from MediatR

SliceDispatch uses the same core interfaces as MediatR (IRequest, IRequestHandler, IPipelineBehavior), making migration straightforward.

Step 1: Install SliceDispatch

dotnet add package SliceDispatch

Step 2: Update Service Registration

Before (MediatR):

services.AddMediatR(cfg =>
{
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
    cfg.AddOpenBehavior(typeof(ValidationBehavior<,>));
});

After (SliceDispatch):

services.AddSliceDispatch(typeof(Program).Assembly);

// Register open-generic behaviors separately for explicit ordering
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));

Step 3: Replace IMediator with ISender

Before:

public class ProductsController : ControllerBase
{
    private readonly IMediator _mediator;

    public ProductsController(IMediator mediator)
    {
        _mediator = mediator;
    }

    public async Task<IActionResult> GetProduct(Guid id)
    {
        var result = await _mediator.Send(new GetProductQuery { Id = id });
        return Ok(result);
    }

    public async Task<IActionResult> CreateProduct(CreateProductCommand command)
    {
        await _mediator.Publish(new ProductCreatedNotification { ProductId = command.Id });
        return Ok();
    }
}

After:

public class ProductsController : ControllerBase
{
    private readonly ISender _sender;

    public ProductsController(ISender sender)
    {
        _sender = sender;
    }

    public async Task<IActionResult> GetProduct(Guid id)
    {
        var result = await _sender.Send(new GetProductQuery { Id = id });
        return Ok(result);
    }

    public async Task<IActionResult> CreateProduct(CreateProductCommand command)
    {
        await _sender.Publish(new ProductCreatedNotification { ProductId = command.Id });
        return Ok();
    }
}

Step 4: Update Using Statements

Replace using MediatR; with using SliceDispatch; in your files.

What Stays the Same

✅ Request and handler interfaces (IRequest<T>, IRequestHandler<TRequest, TResponse>)
✅ Notification and handler interfaces (INotification, INotificationHandler<TNotification>)
✅ Pipeline behavior interface (IPipelineBehavior<TRequest, TResponse>)
✅ Handler implementations (no code changes needed)
✅ FluentValidation integration works identically
✅ All existing business logic

What's Different

🔄 Notification pipeline behaviors use INotificationPipelineBehavior<TNotification> instead of IPipelineBehavior<TNotification>
🔄 No IPublisher interface - use ISender.Publish() instead of IMediator.Publish()
🔄 Notification handlers execute sequentially (no parallel strategy option)

Migration Checklist

  • Install SliceDispatch package
  • Replace AddMediatR() with AddSliceDispatch()
  • Move open-generic behaviors to separate AddTransient() registrations
  • Replace IMediator with ISender in constructors
  • Replace IPublisher with ISender (if used separately)
  • Update notification pipeline behaviors to use INotificationPipelineBehavior<TNotification>
  • Update using MediatR; to using SliceDispatch;
  • Remove MediatR package (optional)
  • Test your pipeline behaviors

License

MIT

Product 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 is compatible.  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.2.0 175 7/5/2026
1.1.0 116 6/20/2026
1.0.5 119 6/20/2026
1.0.4 107 6/20/2026
1.0.3 108 6/20/2026
1.0.1 114 6/20/2026
1.0.0 112 6/20/2026
0.2.0-alpha.2 64 6/20/2026