ConduitR 1.0.2

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

ConduitR

Lightweight, fast, and familiar mediator for .NET — designed to feel natural for MediatR users, with a focus on performance, simplicity, and great DX.

Build NuGet License

Why ConduitR?

  • Familiar: same mental model as MediatR (IRequest<T>, IRequestHandler, notifications, behaviors).
  • Fast: hot path optimized (cached pipelines per request type, low allocations, ValueTask).
  • Modular: add-on packages for Validation, AspNetCore helpers, Processing, and Resilience (Polly).
  • Observable: built-in ActivitySource for OpenTelemetry (Send, Publish, Stream).

Packages

Package What it adds
ConduitR Core mediator + telemetry
ConduitR.Abstractions Public contracts (requests, handlers, behaviors, etc.)
ConduitR.DependencyInjection AddConduit(...) + assembly scanning
ConduitR.Validation.FluentValidation Validation behavior + DI helpers (optional)
ConduitR.AspNetCore ProblemDetails middleware + Minimal API helpers (optional)
ConduitR.Processing Pre/Post processors as behaviors (optional)
ConduitR.Resilience.Polly Retry / Timeout / CircuitBreaker behaviors (optional)

Install

dotnet add package ConduitR
dotnet add package ConduitR.Abstractions
dotnet add package ConduitR.DependencyInjection

# Optional add-ons
dotnet add package ConduitR.Validation.FluentValidation
dotnet add package ConduitR.AspNetCore
dotnet add package ConduitR.Processing
dotnet add package ConduitR.Resilience.Polly

Quick start

// Program.cs
using System.Reflection;
using ConduitR;
using ConduitR.Abstractions;
using ConduitR.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddConduit(cfg =>
{
    cfg.AddHandlersFromAssemblies(Assembly.GetExecutingAssembly());
    cfg.PublishStrategy = PublishStrategy.Parallel; // Parallel (default), Sequential, StopOnFirstException
});

var app = builder.Build();
app.Run();

// A request + handler
public sealed record Ping(string Name) : IRequest<string>;

public sealed class PingHandler : IRequestHandler<Ping, string>
{
    public ValueTask<string> Handle(Ping request, CancellationToken ct)
        => ValueTask.FromResult($"Hello, {request.Name}!");
}

Use the mediator anywhere (DI):

var result = await mediator.Send(new Ping("ConduitR"));

Notifications (Publish)

public sealed record UserRegistered(string Email) : INotification;

public sealed class SendWelcomeEmail : INotificationHandler<UserRegistered> { /* ... */ }
public sealed class AuditLog : INotificationHandler<UserRegistered> { /* ... */ }

// Strategy via AddConduit(...):
// Parallel (default), Sequential (aggregate errors), StopOnFirstException (short-circuit)
await mediator.Publish(new UserRegistered("x@y.com"));

Streaming

public sealed record Ticks(int Count) : IStreamRequest<string>;

public sealed class TicksHandler : IStreamRequestHandler<Ticks, string>
{
    public async IAsyncEnumerable<string> Handle(Ticks req, [EnumeratorCancellation] CancellationToken ct)
    {
        for (var i = 1; i <= req.Count; i++) { ct.ThrowIfCancellationRequested(); await Task.Delay(100, ct); yield return $"tick-{i}"; }
    }
}

// Consume
await foreach (var s in mediator.CreateStream(new Ticks(3))) Console.WriteLine(s);

Validation (FluentValidation)

using ConduitR.Validation.FluentValidation;

builder.Services.AddConduitValidation(typeof(Program).Assembly);

public sealed record CreateOrder(string? Sku, int Qty) : IRequest<string>;
public sealed class CreateOrderValidator : AbstractValidator<CreateOrder>
{
    public CreateOrderValidator() { RuleFor(x => x.Sku).NotEmpty(); RuleFor(x => x.Qty).GreaterThan(0); }
}

AspNetCore helpers

using ConduitR.AspNetCore;

// ProblemDetails (400s for validation, 5xx handled)
builder.Services.AddConduitProblemDetails();

var app = builder.Build();
app.UseConduitProblemDetails();

// Minimal API mapper
app.MapMediatorPost<CreateOrder, string>("/orders");

Pre/Post processors

using ConduitR.Processing;

builder.Services.AddConduitProcessing(typeof(Program).Assembly);

public sealed class AuditPre : IRequestPreProcessor<CreateOrder>
{
    public Task Process(CreateOrder req, CancellationToken ct) { /* audit */ return Task.CompletedTask; }
}

public sealed class MetricsPost : IRequestPostProcessor<CreateOrder, string>
{
    public Task Process(CreateOrder req, string res, CancellationToken ct) { /* metrics */ return Task.CompletedTask; }
}

Resilience (Polly)

using ConduitR.Resilience.Polly;

builder.Services.AddConduitResiliencePolly(o =>
{
    o.RetryCount = 3;                    // exponential backoff
    o.Timeout = TimeSpan.FromSeconds(1); // per-attempt timeout (pessimistic)
    o.CircuitBreakerEnabled = true;
    o.CircuitBreakerFailures = 5;
    o.CircuitBreakerDuration = TimeSpan.FromSeconds(30);
});

Telemetry (OpenTelemetry)

using ConduitR;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

builder.Services.AddOpenTelemetry()
    .ConfigureResource(r => r.AddService("YourApp"))
    .WithTracing(b => b
        .AddSource(ConduitRTelemetry.ActivitySourceName) // "ConduitR"
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddOtlpExporter());

Spans: Mediator.Send, Mediator.Publish, Mediator.Stream (+ tags & exception events).

Performance

  • Cached Send/Stream pipelines per (TRequest,TResponse) (no per-call reflection/delegate build).
  • Lean publish path, minimal allocations.
  • ValueTask in core, one-type-per-file organization.

See docs/perf-pipeline-cache.md for details.

Migrate from MediatR

MediatR ConduitR
IMediator.Send, .Publish same
IRequest<TResponse> same
IRequestHandler<TReq,TRes> same
INotification, INotificationHandler<T> same
Pipeline behaviors same (IPipelineBehavior<TReq,TRes>)
Pre/Post processors via ConduitR.Processing (behaviors)
Validation (FluentValidation) ConduitR.Validation.FluentValidation
ASP.NET Core helpers ConduitR.AspNetCore
Resilience ConduitR.Resilience.Polly

Samples

  • samples/Samples.WebApi — minimal API, ProblemDetails, validation, and streaming endpoint.

Versioning & Releases

  • Semantic versioning. Latest stable: 1.0.2
  • See GitHub Releases for notes.

Contributing

PRs welcome! Dev loop:

dotnet restore
dotnet build
dotnet test

(Optional) microbenchmarks:

dotnet run -c Release --project benchmarks/ConduitR.Benchmarks/ConduitR.Benchmarks.csproj

License

MIT — see LICENSE.

Commit it

git add README.md
git commit -m "docs: refresh README with streaming, publish strategies, processing, resilience, telemetry, perf"
git push
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 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 (1)

Showing the top 1 NuGet packages that depend on ConduitR:

Package Downloads
ConduitR.DependencyInjection

Microsoft.Extensions.DependencyInjection integration for ConduitR.

GitHub repositories

This package is not used by any popular GitHub repositories.