REPR 10.2.1

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

REPR

Rest EndPoint Response REPR is a lightweight library that implements the Mediator design pattern in .NET. It helps decouple the sender of a request from its handler, promoting cleaner architecture and separation of concerns. Instead of calling services or logic directly, you send a request (like a command or query), and REPR routes it to the appropriate handler.

REPR optimizes dependency injection life cycles by supporting Transient, Scoped, and Singleton handlers.

Table of Contents

Use

Initialization

To register REPR handlers, extend desired handler and use AddREPR() to service collection. REPR will find any type with a REPR handler and automatically add that handler to the appropriate life cycle during startup. Only performance hit to your application will be during startup. By default REPR only adds types from the executing assembly. To specify an assembly in the app domain but not in the executing assembly i.e. a common library assembly. You must include the assembly name in the REPROptions.FilterAssemblies AND REPROptions.IncludeAppDomainAssemblies to true.

Add to service collection
services.AddREPR();
REPR Options
public sealed record REPROptions
{
    public bool IncludeAppDomainAssemblies { get; set; }
    public IEnumerable<string>? FilteredAssemblies { get; set; }
    public bool StrictMode { get; set; }
    public bool UseSourceGeneration { get; set; }
    public ILogger? Logger { get; set; }
    public bool Verbose { get; set; }
}
  • IncludeAppDomainAssemblies: will use AppDomain.CurrentDomain.GetAssemblies() if set to true and Assembly.GetExecutingAssembly() if set to false. Note: If you set IncludeAppDomainAssemblies to true and do not include a FilteredAssemblies an exception will be thrown.
  • FilteredAssemblies: limits the scope of assemblies during search and startup. This is highly recommended for large applications.
  • StrictMode: optional and primarily stylistic. Setting this to true will require the handler to be internal and sealed. This is a runtime error, so be careful. One day I would like to add an SCA to this repo.
  • UseSourceGeneration: enables compile-time source generation for handler registration, eliminating runtime reflection. See Source Generation for details.
  • Logger: optional ILogger used by REPR. When set together with Verbose, REPR logs the resolved handler type. See Verbose Logging for details.
  • Verbose: when true and Logger is set, REPR logs the full type name of each resolved handler, job, and subscriber on dispatch.
Verbose Logging

Provide an ILogger and set Verbose to true to have REPR log the full type name of every handler, job, and notification subscriber it resolves at dispatch time. Both options must be set; if either is missing, no logging occurs.

services.AddREPR<Program>(options =>
{
    options.Logger = loggerFactory.CreateLogger("REPR");
    options.Verbose = true;
});

Each dispatch logs an information-level entry such as:

REPR resolved type: Test.REPR.Library.TransientHandler

For ExecuteJob and Send, every resolved job or subscriber is logged individually.

Source Generation

REPR supports compile-time source generation as an alternative to runtime reflection. This provides:

  • Zero runtime reflection - all handler registration is generated at compile time
  • Faster startup - no assembly scanning at runtime
  • AOT compatibility - works with Native AOT compilation
Enabling Source Generation

The source generator is bundled with the REPR package. To use it:

services.AddREPR<Program>(options =>
{
    options.UseSourceGeneration = true;
});
How It Works

When UseSourceGeneration is enabled, the source generator:

  1. Scans your code at compile time for classes implementing REPR handler interfaces
  2. Generates a REPRRegistration class with an AddREPRHandlers() method
  3. Groups handlers by lifecycle (Singleton, Scoped, Transient) and sorts them alphabetically
  4. Registers each handler with the appropriate DI lifetime
Generated Code Example

The generator produces code similar to:

// <auto-generated/>
public static class REPRRegistration
{
    public static IServiceCollection AddREPRHandlers(this IServiceCollection services)
    {
        // Singleton handlers
        services.AddSingleton<IRequestHandler<CacheRequest, CacheResponse>, CacheHandler>();
        
        // Scoped handlers
        services.AddScoped<IRequestHandler<OrderRequest, OrderResponse>, OrderHandler>();
        
        // Transient handlers
        services.AddTransient<IRequestHandler<ValidationRequest, ValidationResponse>, ValidationHandler>();
        
        return services;
    }
}

Note: The IREPR mediator itself is registered by AddREPR() in the REPR library, not by the generated code. Its implementation type is internal to the REPR assembly, so registering it from generated code (compiled into your assembly) would fail with CS0122.

When to Use Source Generation
Scenario Recommendation
Native AOT deployment Use source generation
Large applications with many handlers Use source generation for faster startup
Small applications Either works well
Need runtime assembly scanning Use reflection (default)

REPR Handlers

REPR handlers default to transient life cycle if base interface is used. To specify a life cycle extend the appropriate interface.

Handlers

  • IRequestHandler<TRequest, TResponse>
  • ICommandQueryRequestHandler<TCommand, TQuery, TResponse>
  • IListRequestHandler<TResponse>
  • IJobHandler<TRequest, TResponse>
  • INotificationHandler<TRequest>
IRequestHandler

IRequestHandler will search for a single implementation of IRequestHandler<TRequest, TResponse> and execute. REPR will throw an exception if none are found.

  • Base (Transient): IRequestHandler<TRequest, TResponse>
  • Singleton: ISingletonRequestHandler<TRequest, TResponse>
  • Scoped: IScopedRequestHandler<TRequest, TResponse>
  • Transient: ITransientRequestHandler<TRequest, TResponse>
Example IRequestHandler Handler
using REPR.Handlers;

namespace Test.REPR.Library;

internal sealed class TransientHandler : ITransientRequestHandler<TransientHandlerRequest, TransientHandlerResponse>
{
    public Task<TransientHandlerResponse> Handle(TransientHandlerRequest request, CancellationToken cancellationToken)
    {
        return Task.FromResult(new TransientHandlerResponse());
    }
}
Example GET Controller
private readonly IREPR _repr;

public FooController(IREPR repr)
{
    _repr = repr;
}

[HttpGet]
public async Task<IActionResult> Get(TRequest request, CancellationToken cancellationToken)
{
    var response = await _repr.Handle<TRequest, TResponse>(request, cancellationToken);
    return Ok(response);
}
IListHandler

IListHandler will search for a single implementation of IListRequestHandler<TResponse> and execute. REPR will throw an exception if none are found.

  • Base (Transient): IListRequestHandler<TResponse>
  • Singleton: ISingletonListRequestHandler<TResponse>
  • Scoped: IScopedListRequestHandler<TResponse>
  • Transient: ITransientListRequestHandler<TResponse>
Example List Handler
using REPR.Handlers;

namespace Test.REPR.Library.Handlers;

internal sealed class TransientListHandler : ITransientListRequestHandler<TransientListHandlerResponse>
{
    public Task<IEnumerable<TransientListHandlerResponse>> Handle(CancellationToken cancellationToken)
    {
        return Task.FromResult<IEnumerable<TransientListHandlerResponse>>([]);
    }
}
Example List (GET) Controller
private readonly IREPR _repr;

public FooController(IREPR repr)
{
    _repr = repr;
}

[HttpGet]
public async Task<IActionResult> List(CancellationToken cancellationToken)
{
    var response = await _repr.Handle<TResponse>(request, cancellationToken);
    return Ok(response);
}
ITransientCommandQueryHandler

ITransientCommandQueryHandler will search for a single implementation of ICommandQueryRequestHandler<TCommand, TQuery, TResponse> and execute. REPR will throw an exception if none are found.

  • Base (Transient): ICommandQueryRequestHandler<TCommand, TQuery, TResponse>
  • Singleton: ISingletonCommandQueryRequestHandler<TCommand, TQuery, TResponse>
  • Scoped: IScopedCommandQueryRequestHandler<TCommand, TQuery, TResponse>
  • Transient: ITransientCommandQueryRequestHandler<TCommand, TQuery, TResponse>
Example Command Query Handler
using REPR.Handlers;

namespace Test.REPR.Library;

internal sealed class TransientCommandQueryHandler : ITransientCommandQueryRequestHandler<TransientCommandQueryHandlerCommand, TransientCommandQueryHandlerQuery, TransientCommandQueryHandlerResponse>
{
    public Task<TransientCommandQueryHandlerResponse> Handle(TransientCommandQueryHandlerCommand command, TransientCommandQueryHandlerQuery query, CancellationToken cancellationToken)
    {
        return Task.FromResult(new TransientCommandQueryHandlerResponse());
    }
}
Example POST Controller
private readonly IREPR _repr;

public FooController(IREPR repr)
{
    _repr = repr;
}

[HttpPost]
public async Task<IActionResult> Post(TCommand command, TQuery query, CancellationToken cancellationToken)
{
    var response = await _repr.Handle<TCommand, TQuery, TResponse>(command, query, cancellationToken);
    return Ok(response);
}
IJobHandler

IJobHandler will search for a ALL implementation of IJobHandler<TRequest, TResponse> and execute. REPR will throw an exception if none are found.

  • Base (Transient): IJobHandler<TRequest, TResponse>
  • Singleton: ISingletonJobHandler<TRequest, TResponse>
  • Scoped: IScopedJobHandler<TRequest, TResponse>
  • Transient: ITransientJobHandler<TRequest, TResponse>
Example Job Handler
using REPR.Handlers;
using Test.REPR.Library.Handlers.Jobs;

namespace Test.REPR.Library;

internal sealed class ScopedJobHandlerOne : IScopedJobHandler<ScopedJobRequest, ScopedJobResponse>
{
    public Task<ScopedJobResponse> Execute(ScopedJobRequest request, CancellationToken cancellationToken)
    {
        return Task.FromResult(new ScopedJobResponse());
    }
}
Example POST Jobs Controller
private readonly IREPR _repr;

public FooController(IREPR repr)
{
    _repr = repr;
}

[HttpPost]
public async Task<IActionResult> Post(TRequest request, CancellationToken cancellationToken)
{
    var response = await _repr.ExecuteJob<TRequest, TResponse>(command, cancellationToken);
    return Ok(response);
}
INotificationHandler

IJobHandler will search for a ALL implementation of INotificationHandler<TRequest> and execute. REPR will throw an exception if none are found.

  • Base (Transient): INotificationHandler<TRequest>
  • Singleton: ISingletonNotificationHandler<TRequest>
  • Scoped: IScopedNotificationHandler<TRequest>
  • Transient: IScopedNotificationHandler<TRequest>
Example Notification Handler
using REPR.Handlers;
using Test.REPR.Library.Handlers.NotificationRequests;

namespace Test.REPR.Library.Handlers;

internal sealed class TransientNotificationHandlerOne : ITransientNotificationHandler<TransientNotificationRequest>
{
    public Task Send(TransientNotificationRequest request, CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}

Example POST Notification Controller
private readonly IREPR _repr;

public FooController(IREPR repr)
{
    _repr = repr;
}

[HttpPost]
public async Task<IActionResult> Post(TRequest request, CancellationToken cancellationToken)
{
    await _repr.Send<TRequest>(request, cancellationToken);
    return Ok();
}
Product Compatible and additional computed target framework versions.
.NET 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
10.2.1 123 7/6/2026 10.2.1 is deprecated because it has critical bugs.
10.2.0 115 7/2/2026
10.1.0 152 1/30/2026
9.1.0 213 7/30/2025
9.0.2 257 5/28/2025
9.0.1 243 5/28/2025
9.0.0 235 5/28/2025
8.1.0 196 7/30/2025
8.0.2 245 5/28/2025