Start Debugging

MediatR vs einfache Service-Klassen in 2026: Sollte Sie die Lizenzänderung umstimmen?

MediatR-Dispatch und Pipeline-Behaviors mit direkten Dienstaufrufen vergleichen: DI-Validierung, Lizenzkriterien und Leistungsmessung.

Diese Website nutzt Walter, einen KI-Agenten, für Recherche, Texte und Pflege. Redaktion und Überprüfung

Direkte Dienste passen gut, wenn jede Anfrage ein eindeutiges Ziel hat. MediatR lohnt sich eher, wenn mehrere Anfragetypen dieselben Pipeline-Behaviors nutzen oder Benachrichtigungen an mehrere Empfänger senden. Prüfe die Lizenzbedingungen der verwendeten Version gesondert.

Korrektur — 2. November 2026: DI-Validierung und Lizenzvergleich korrigiert sowie unbelegte Zeit- und Allokationsangaben entfernt.

Die Beispiele zielen auf net11.0 und zeigen die Verträge von MediatR 13/14. Die unten beschriebene DI-Prüfung lief mit .NET 10.

Was sich im Juli 2025 tatsächlich geändert hat

MediatR 12.5.0 erschien unter Apache 2.0. Der Autor kündigte im Juli 2025 mit Version 13.0.0 RPL-1.5 und kommerzielle Optionen an. Frühere Versionen behalten ihre ursprünglichen Bedingungen.

Zu den Community-Kriterien von Lucky Penny Software gehören ein jährlicher Bruttoumsatz oder ein gemeinnütziges Budget unter 5 Millionen US-Dollar und höchstens 10 Millionen US-Dollar externes Kapital. Der Anbieter nennt weitere Bedingungen für staatliche oder staatsnahe Einrichtungen, Hochschulnutzung und Kundenaufträge. Prüfe die Kriterien für Organisation und Verwendungszweck.

Die Feature-Matrix auf einen Blick

AspektMediatR 13+Einfache Service-Klassen
Request-VersandIndirektion über ISender.SendDirekter Methodenaufruf an einer injizierten Schnittstelle
QuerschnittsbelangeIPipelineBehavior<,> als First-Class-KonstruktDecorator (Scrutor) oder explizite Aufrufe
DI-ValidierungAbhängig von Registrierungen und ProvideroptionenAbhängig von Registrierungen und Provideroptionen
Benachrichtigungen / Fan-outIntegriertes INotification-PublishHandgeschriebene Handler-Liste

Der zentrale Entwurfsunterschied liegt in der Pipeline: Viele Anfragen können Validierung, Logging, Transaktionen oder Caching gemeinsam nutzen.

Wie MediatR aussieht und das einfache Äquivalent

Dies ist die kanonische MediatR-Form: ein Request, ein Handler und ein Aufrufer, der über ISender versendet.

// .NET 11, C# 14, MediatR 13+ - request + handler
using MediatR;

public record GetOrderById(int OrderId) : IRequest<OrderDto>;

public sealed class GetOrderByIdHandler(AppDbContext db)
    : IRequestHandler<GetOrderById, OrderDto>
{
    public async Task<OrderDto> Handle(GetOrderById request, CancellationToken ct)
    {
        var order = await db.Orders.FindAsync([request.OrderId], ct)
            ?? throw new OrderNotFoundException(request.OrderId);
        return order.ToDto();
    }
}

// In an endpoint:
public async Task<OrderDto> Get(int id, ISender sender, CancellationToken ct)
    => await sender.Send(new GetOrderById(id), ct);

Die einfache Version fasst Request und Handler in einer einzigen Methode eines injizierten Service zusammen. Der Aufrufer hängt direkt von der Schnittstelle ab.

// .NET 11, C# 14 - plain service class, no MediatR
public interface IOrderService
{
    Task<OrderDto> GetByIdAsync(int orderId, CancellationToken ct);
}

public sealed class OrderService(AppDbContext db) : IOrderService
{
    public async Task<OrderDto> GetByIdAsync(int orderId, CancellationToken ct)
    {
        var order = await db.Orders.FindAsync([orderId], ct)
            ?? throw new OrderNotFoundException(orderId);
        return order.ToDto();
    }
}

// In an endpoint:
public async Task<OrderDto> Get(int id, IOrderService orders, CancellationToken ct)
    => await orders.GetByIdAsync(id, ct);

Bei einem Interface-Aufruf führt der Sprung zur Definition zur Interface-Methode. Die IDE kann deren Implementierungen anzeigen. Bei Send suche nach dem Handler des Anfragetyps.

AddScoped registriert einen Dienst. Beim integrierten Provider kann ValidateOnBuild eine fehlende Konstruktorabhängigkeit eines registrierten Dienstes beim Erstellen des Hosts erkennen. Ohne Validierung kann der Build gelingen und erst die Dienstauflösung scheitern. Die Standardwerte von ASP.NET Core hängen von der Umgebung ab. ValidateScopes prüft separat die Lebensdauern.

Für diese Korrektur wurde unter .NET 10 ein Scoped-Dienst mit fehlender Konstruktorabhängigkeit registriert. Mit den Standardwerten scheiterte der Development-Host bei Build(), der Production-Host erst bei der Auflösung. Eine explizite Einstellung von ValidateOnBuild änderte die Fehlerphase.

// .NET 11, C# 14 - registration, side by side
// MediatR: scan an assembly, register every handler reflectively
builder.Services.AddMediatR(cfg =>
    cfg.RegisterServicesFromAssemblyContaining<GetOrderById>());

// Plain: explicitly register the scoped service
builder.Services.AddScoped<IOrderService, OrderService>();

Das Pipeline Behavior und wie man ohne es lebt

Hier verdient MediatR seinen Platz. Ein Pipeline Behavior umschließt jeden Request, was Ihnen genau einen Ort gibt, um Validierung, Logging, Zeitmessung oder eine Transaktion hinzuzufügen.

// .NET 11, C# 14, MediatR 13+ - cross-cutting validation for ALL requests
using MediatR;

public sealed class ValidationBehavior<TRequest, TResponse>(
    IEnumerable<IValidator<TRequest>> validators)
    : IPipelineBehavior<TRequest, TResponse>
    where TRequest : notnull
{
    public async Task<TResponse> Handle(
        TRequest request,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken ct)
    {
        foreach (var validator in validators)
            await validator.ValidateAndThrowAsync(request, ct);
        return await next(ct);
    }
}

Einmal registriert, läuft dieses Behavior vor jedem Handler. Dies durch kopierte und eingefügte Validierungsaufrufe in jedem einfachen Service zu ersetzen, wäre eine Regression, und es ist das beste Argument, einen Mediator zu behalten. Aber Sie können dieselbe “alles umschließen”-Eigenschaft in einfachem DI mit dem Decorator-Muster erhalten, indem Sie Scrutor (MIT-lizenziert) verwenden, um einen Decorator um eine Schnittstelle zu registrieren:

// .NET 11, C# 14, Scrutor 6.x - a decorator gives you the same cross-cutting hook
public sealed class LoggingOrderService(
    IOrderService inner,
    ILogger<LoggingOrderService> logger) : IOrderService
{
    public async Task<OrderDto> GetByIdAsync(int orderId, CancellationToken ct)
    {
        logger.LogInformation("Fetching order {OrderId}", orderId);
        return await inner.GetByIdAsync(orderId, ct);
    }
}

// Registration: decorate the real implementation
builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.Decorate<IOrderService, LoggingOrderService>();

Der Kompromiss ist ehrlich: MediatRs Behavior ist generisch über jeden Request mit einer einzigen Registrierung, während ein Decorator pro Schnittstelle gilt. Wenn Sie ein oder zwei Querschnittsbelange und eine Handvoll Service-Schnittstellen haben, gewinnen Decorator an Klarheit. Wenn Sie zehn Behaviors haben, die einheitlich auf zweihundert Request-Typen anzuwenden sind, ist MediatRs generische Pipeline tatsächlich weniger Code, und das ist das Szenario, in dem Bleiben (und Zahlen oder die Qualifikation für Community) vertretbar ist. Für Belange auf Request-Ebene, die in Wirklichkeit HTTP-Belange sind, beachten Sie, dass ein Teil dessen, was Leute in Behaviors stecken, eher in Middleware oder Filter gehört, dieselbe Fläche, die einen globalen Ausnahmefilter in ASP.NET Core 11 hinzufügen abdeckt.

Was der Versand tatsächlich kostet

Send löst einen Handler auf und durchläuft die konfigurierte Pipeline. Ein direkter Aufruf verwendet den injizierten Dienst. Für diese Pfade gibt es hier keinen vergleichbaren Benchmark, daher werden keine Zeiten oder Allokationen pro Aufruf angegeben.

Wenn Dispatch-Kosten die Entscheidung beeinflussen, miss beide Pfade mit gleicher Handler-Arbeit, Lebensdauer, Laufzeit und Behavior-Kette. Miss Registrierung und Kaltstart getrennt. Explizite Registrierung kann den Handler-Scan vermeiden. Prüfe Trimming und Native AOT mit der veröffentlichten Anwendung.

Das Detail, das für Sie entscheidet

Einige Einschränkungen klären das, bevor architektonischer Geschmack ins Spiel kommt.

Eine auf 12.5.0 festgelegte Anwendung bleibt bei dieser Version und deren Bedingungen. Prüfe vor einer dauerhaften Festlegung den Wartungsbedarf.

Wenn Sie nur jemals Send aufrufen, brauchen Sie keinen Mediator. Der ehrliche Test: Öffnen Sie Ihre Solution und suchen Sie nach IPipelineBehavior und INotification. Wenn es null Behaviors gibt und Sie keine Benachrichtigungen veröffentlichen, fungiert MediatR als Indirektionsschicht über Methodenaufrufen, und es zu entfernen ist ein mechanisches Refactoring, das den Code navigierbarer macht, eine Abhängigkeit entfernt und die Lizenzfrage in einem Zug löscht. Das ist derselbe Instinkt zur Bibliotheksrationalisierung, der dahintersteckt, die mitgelieferte Option in System.Text.Json vs Newtonsoft.Json in 2026 zu wählen.

Die Entscheidung, in einer Zeile

Für den direkten Aufruf eines Dienstes bietet sich explizite Injektion an. Behalte MediatR, wenn mehrere Anfragetypen von der gemeinsamen Pipeline oder Benachrichtigungen profitieren. Nach der Entscheidung für Dienste hilft die Migrationsanleitung.

Verwandt

Quellen

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.