Mediator.Switch 1.0.0-preview1

This is a prerelease version of Mediator.Switch.
There is a newer version of this package available.
See the version list below for details.
dotnet add package Mediator.Switch --version 1.0.0-preview1
                    
NuGet\Install-Package Mediator.Switch -Version 1.0.0-preview1
                    
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="Mediator.Switch" Version="1.0.0-preview1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Mediator.Switch" Version="1.0.0-preview1" />
                    
Directory.Packages.props
<PackageReference Include="Mediator.Switch" />
                    
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 Mediator.Switch --version 1.0.0-preview1
                    
#r "nuget: Mediator.Switch, 1.0.0-preview1"
                    
#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.
#addin nuget:?package=Mediator.Switch&version=1.0.0-preview1&prerelease
                    
Install Mediator.Switch as a Cake Addin
#tool nuget:?package=Mediator.Switch&version=1.0.0-preview1&prerelease
                    
Install Mediator.Switch as a Cake Tool

SwitchMediator

SwitchMediator: A Blazing Fast, Source-Generated Mediator for .NET

SwitchMediator provides a high-performance implementation of the mediator pattern, offering an API surface familiar to users of popular libraries like MediatR. By leveraging C# Source Generators, SwitchMediator eliminates runtime reflection for handler discovery and dispatch, instead generating highly optimized switch statements at compile time. We also want you to Switch your Mediator to ours, get it? 😉

The result? Faster execution, improved startup times, step-into debuggability, and compile-time safety.


Table of Contents


Why SwitchMediator?

Traditional mediator implementations often rely on runtime reflection to discover handlers and construct pipelines. While flexible, this approach can introduce overhead:

  • Runtime Performance Cost: Reflection and dictionary lookups add latency to every request dispatch.
  • Startup Delay: Scanning assemblies and building internal mappings takes time when your application starts.
  • Debugging Complexity: Stepping through the mediator logic can involve navigating complex internal library code, delegates, and reflection calls.
  • Trimming/AOT Challenges: Reflection can make code less friendly to .NET trimming and Ahead-of-Time (AOT) compilation scenarios.

SwitchMediator tackles these issues head-on by moving the work to compile time. The source generator creates explicit C# code with direct handler calls and optimized switch statements, offering a "pay-as-you-go" approach where the cost is incurred during build, not at runtime.

Key Advantages Over Reflection-Based Mediators

  • 🚀 Maximum Performance: Eliminates runtime reflection and dictionary lookups for dispatch. Uses compile-time switch statements and direct method calls. Ideal for performance-critical paths and high-throughput applications.
  • 🧐 Enhanced Debuggability: You can directly step into the generated SwitchMediator.g.cs file! See the exact switch statement matching your request, observe the explicit nesting of pipeline behavior calls (await next(...)), and step directly into your handler code. This provides unparalleled transparency compared to debugging reflection-based dispatch logic.
  • Compile-Time Safety: Handler discovery happens during the build. Missing request handlers result in build errors, not runtime exceptions, catching issues earlier in the development cycle.
  • ⏱️ Faster Startup: Less work (like assembly scanning for handlers) needs to happen when your application boots up.
  • ✂️ Trimming / AOT Friendly: Avoids runtime reflection, making the dispatch mechanism inherently more compatible with .NET trimming and AOT compilation. (Note: Ensure handlers and dependencies are also trimming/AOT safe).
  • 🔍 Explicitness: The generated code serves as clear, inspectable documentation of how requests are routed and pipelines are constructed for each message type.

Features

  • Request/Response messages (IRequest<TResponse>, IRequestHandler<TRequest, TResponse>)
  • Notification messages (INotification, INotificationHandler<TNotification>)
  • Pipeline Behaviors (IPipelineBehavior<TRequest, TResponse>) for cross-cutting concerns.
  • Flexible Pipeline Behavior Ordering via [PipelineBehaviorOrder(int order)].
  • Pipeline Behavior Constraints using standard C# generic constraints (where TRequest : ...).
  • Explicit Notification Handler Ordering via DI configuration.
  • Seamless integration with Microsoft.Extensions.DependencyInjection.

Installation

You'll typically need two packages:

  1. Mediator.Switch: Contains the core interfaces (IRequest, INotification, IPipelineBehavior, etc.) and the source generator itself.
  2. Mediator.Switch.Extensions.Microsoft.DependencyInjection: Provides extension methods for easy registration with the standard .NET DI container.
dotnet add package Mediator.Switch
dotnet add package Mediator.Switch.Extensions.Microsoft.DependencyInjection

Usage Example

This example assumes you have defined your IRequest, INotification, IRequestHandler, INotificationHandler, and IPipelineBehavior types in your project (e.g., in the Sample namespace and Program's assembly).

DI Setup

Register SwitchMediator and its dependencies (handlers, behaviors, validators) in your application's composition root (e.g., Program.cs).

using Microsoft.Extensions.DependencyInjection;
using Mediator.Switch;
using Mediator.Switch.Extensions.Microsoft.DependencyInjection;
using System;
using System.Threading.Tasks;

public static class Program
{
    public static async Task Main()
    {
        var services = new ServiceCollection();

        // --- SwitchMediator Registration ---
        // 1. Register SwitchMediator itself.
        // 2. Pass assembly(s) containing handlers, messages, behaviors for scanning.
        services.AddScoped<SwitchMediator>(typeof(Program).Assembly)
            // 3. Optionally, specify notification handler order.
            .OrderNotificationHandlers<Sample.UserLoggedInEvent>(
                typeof(Sample.UserLoggedInLogger),
                typeof(Sample.UserLoggedInAnalytics)
            );

        // --- Build and Scope ---
        var serviceProvider = services.BuildServiceProvider();
        using var scope = serviceProvider.CreateScope(); // Simulate a request scope

        var sender = scope.ServiceProvider.GetRequiredService<ISender>();
        var publisher = scope.ServiceProvider.GetRequiredService<IPublisher>();

        // --- Execute Logic ---
        await RunSampleLogic(sender, publisher);
    }

    public static async Task RunSample(ISender sender, IPublisher publisher)
    {
        // See next section
    }
}

Sending Requests & Publishing Notifications

Inject ISender and IPublisher into your services (controllers, etc.) and use them to dispatch messages.

// Inside the RunSampleLogic method from above

public static async Task RunSample(ISender sender, IPublisher publisher)
{
    Console.WriteLine("--- Sending GetUserRequest ---");
    string userResult = await sender.Send(new Sample.GetUserRequest(123));
    Console.WriteLine($"--> Result: {userResult}\n");

    Console.WriteLine("--- Sending CreateOrderRequest ---");
    int orderResult = await sender.Send(new Sample.CreateOrderRequest("Gadget"));
    Console.WriteLine($"--> Result: {orderResult}\n");

    Console.WriteLine("--- Publishing UserLoggedInEvent ---");
    await publisher.Publish(new Sample.UserLoggedInEvent(123));
    Console.WriteLine("--- Notification Published ---\n");

    Console.WriteLine("--- Sending Request with Validation Failure ---");
    try
    {
        await sender.Send(new Sample.GetUserRequest(-1));
    }
    catch (FluentValidation.ValidationException ex)
    {
        Console.WriteLine($"--> Caught Expected ValidationException: {ex.Errors.FirstOrDefault()?.ErrorMessage}\n");
    }
}

Example Output

From Sample.Program:

--- Sending GetUserRequest ---
Logging: Handling GetUserRequest
Audit: Processing request at 27/3/2025 3:53:07 pm
Audit: Completed request at 27/3/2025 3:53:07 pm
Logging: Handled GetUserRequest
--> Result: User 123 at 27/3/2025 3:53:07 pm

--- Sending CreateOrderRequest ---
Logging: Handling CreateOrderRequest
Transaction: Starting with ID 0a12d204-8547-41e8-b6ca-d89098081ab6
Transaction: Completed with ID 0a12d204-8547-41e8-b6ca-d89098081ab6
Logging: Handled CreateOrderRequest
--> Result: 42

--- Publishing UserLoggedInEvent ---
Logged: User 123 logged in.
Analytics: User 123 tracked.
--- Notification Published ---

--- Sending GetUserRequest with Validation Failure ---
Logging: Handling GetUserRequest
--> Caught Expected ValidationException: UserId must be positive

--- Sending CreateOrderRequest with Validation Failure ---
Logging: Handling CreateOrderRequest
--> Caught Expected ValidationException: Product cannot be empty
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Mediator.Switch:

Package Downloads
Mediator.Switch.Extensions.Microsoft.DependencyInjection

Microsoft Dependency Injection extensions for Mediator.Switch.

Mediator.Switch.SourceGenerator

A Mediator implementation using source generated C# switch expressions for handler dispatch enabling faster execution, improved startup times, step-into debuggability, and compile-time safety.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.7.0 78 4/16/2025
1.6.0 93 4/14/2025
1.5.0 101 4/12/2025
1.4.0 135 4/11/2025
1.3.1 167 4/10/2025
1.3.1-preview1 159 4/10/2025
1.2.0 164 4/9/2025
1.1.0 161 3/31/2025
1.0.0 162 3/31/2025
1.0.0-preview3 145 3/30/2025
1.0.0-preview2 100 3/29/2025
1.0.0-preview1 138 3/27/2025