SoftwareFirst.Switchboard 1.1.0

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

Switchboard

NuGet CI

A lightweight, MediatR-compatible mediator for .NET.

Switchboard implements the request/response, notification, and pipeline-behavior surface of MediatR on top of Microsoft.Extensions.DependencyInjection, in a few hundred lines of code with a single dependency (Microsoft.Extensions.DependencyInjection.Abstractions). It was extracted from a production system that moved off MediatR when it became commercially licensed: swap your using directives, change one registration call, and your handlers, behaviors, and call sites compile unchanged.

Install

dotnet add package SoftwareFirst.Switchboard

Targets net8.0, net9.0 and net10.0, so you can move off MediatR without moving frameworks first. The package ID is prefixed, but the assembly and namespace are both plain Switchboard — you write using Switchboard;.

Why this one

Several MediatR alternatives exist now, and most compete on speed or feature count. Switchboard competes on being small:

  • A few hundred lines, across about a dozen files you can read end to end in one sitting.
  • One dependencyMicrosoft.Extensions.DependencyInjection.Abstractions, floored at the lowest patch of each major so it never drags your other Microsoft.Extensions.* packages forward.
  • No source generators, analyzers, or build-time magic. Plain reflection over the DI container, the way MediatR does it.
  • A deliberately identical API surface — the migration is a find-and-replace, not a rewrite.
  • Apache 2.0, extracted from a production system that made this exact switch.

If you need streaming, parallel publish strategies, or maximum throughput, a source-generated alternative is the better fit — the migration table below says so explicitly.

Quick start

Define a request and its handler:

using Switchboard;

public sealed record GetOrder(int Id) : IRequest<OrderDto>;

public sealed class GetOrderHandler : IRequestHandler<GetOrder, OrderDto>
{
    public Task<OrderDto> Handle(GetOrder request, CancellationToken cancellationToken)
        => /* ... */;
}

Register the mediator and send:

services.AddSwitchboard(cfg => cfg
    .RegisterServicesFromAssemblyContaining<GetOrderHandler>());
public sealed class OrderController(ISender sender) : ControllerBase
{
    [HttpGet("{id}")]
    public Task<OrderDto> Get(int id, CancellationToken ct) => sender.Send(new GetOrder(id), ct);
}

Void requests implement IRequest (no type argument) and are handled by IRequestHandler<TRequest>.

Pipeline behaviors

Behaviors wrap every handler, outermost first in the order they are added:

public sealed class LoggingBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    public async Task<TResponse> Handle(
        TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        // before
        var response = await next(cancellationToken);
        // after
        return response;
    }
}
services.AddSwitchboard(cfg => cfg
    .RegisterServicesFromAssemblyContaining<GetOrderHandler>()
    .AddOpenBehavior(typeof(LoggingBehaviour<,>))      // runs outermost
    .AddOpenBehavior(typeof(ValidationBehaviour<,>))); // runs inside logging

A behavior that applies to one specific request/response pair goes in with AddBehavior:

services.AddSwitchboard(cfg => cfg
    .RegisterServicesFromAssemblyContaining<GetOrderHandler>()
    .AddOpenBehavior(typeof(LoggingBehaviour<,>))   // outermost
    .AddBehavior<AuditGetOrder>()                   // IPipelineBehavior<GetOrder, OrderDto>
    .AddOpenBehavior(typeof(ValidationBehaviour<,>))); // innermost

Open and closed behaviors share a single ordering, so the first one added is outermost regardless of which kind it is. Registering directly against the container still works too: services.AddTransient<IPipelineBehavior<GetOrder, OrderDto>, MyBehavior>().

Void requests run through the same pipeline with TResponse == Unit, so open-generic behaviors apply to them unchanged.

Notifications

public sealed record OrderPlaced(int OrderId) : INotification;

public sealed class SendReceipt : INotificationHandler<OrderPlaced> { /* ... */ }
public sealed class UpdateStats : INotificationHandler<OrderPlaced> { /* ... */ }
await publisher.Publish(new OrderPlaced(42), cancellationToken);

Handlers run sequentially, in registration order — never in parallel — so they can safely share scoped state such as an EF Core DbContext.

Migrating from MediatR

  1. Replace the MediatR package reference with SoftwareFirst.Switchboard.
  2. Replace using MediatR; with using Switchboard;.
  3. Replace services.AddMediatR(...) with services.AddSwitchboard(...) — the configuration methods (RegisterServicesFromAssemblyContaining, RegisterServicesFromAssembly, AddOpenBehavior) keep their names.
MediatR feature Switchboard
IRequest, IRequest<T>, IRequestHandler<,>, IRequestHandler<> ✅ identical
INotification, INotificationHandler<> ✅ identical
IPipelineBehavior<,> (first registered runs outermost) ✅ identical
ISender, IPublisher, IMediator, Unit ✅ identical
Untyped Send(object) / Publish(object) ✅ identical
Assembly scanning for handlers ✅ identical
Streaming (IStreamRequest<>) ❌ not implemented
Request pre-/post-processors ❌ use a pipeline behavior
Exception handlers/actions (IRequestExceptionHandler) ❌ use a pipeline behavior
Custom publish strategies (parallel, etc.) ❌ sequential only

Semantics worth knowing

  • Cancellation is never lost. The CancellationToken passed to Send flows to every behavior and the handler, even when a behavior calls next() without arguments.
  • Handlers and behaviors are transient; they are resolved from the scope the mediator was resolved from, so scoped dependencies work as expected.
  • Publishing to zero handlers is a no-op, mirroring MediatR.
  • Handler-type wrappers are cached statically per request type; the cache is stateless and thread-safe.

License

Apache 2.0

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 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 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.1.0 0 8/9/2026
1.0.1 220 7/26/2026
1.0.0 98 7/26/2026

Adds net8.0 and net9.0 targets alongside net10.0, so the package can be adopted without a framework upgrade. No API changes.