Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Videos & KonferenzenTwo Minute Papers: Claude Opus 5.5 AI: A Massive Leap Forward(24.09.2026 um 10:40 Uhr)
Sicherheitslücken (CVE)USN-8805-1: Moodle vulnerability(23.09.2026 um 16:43 Uhr)
Sichere ProgrammierungI thought clipboard sync would be simple. Android had other plans.(24.09.2026 um 11:01 Uhr)
Sichere ProgrammierungAI-assisted genealogy, a follow-up(24.09.2026 um 11:02 Uhr)
Sicherheitslücken (CVE)Smart Contract Vulnerability Surface Analysis: HashKey Exchange(24.09.2026 um 11:02 Uhr)
Sichere ProgrammierungAI Agents Calling Your Existing Backend Without MCP Development(24.09.2026 um 11:06 Uhr)
Videos & KonferenzenTwo Minute Papers: Claude Opus 5.5 AI: A Massive Leap Forward(24.09.2026 um 10:40 Uhr)
Sicherheitslücken (CVE)USN-8805-1: Moodle vulnerability(23.09.2026 um 16:43 Uhr)
Sichere ProgrammierungI thought clipboard sync would be simple. Android had other plans.(24.09.2026 um 11:01 Uhr)
Sichere ProgrammierungAI-assisted genealogy, a follow-up(24.09.2026 um 11:02 Uhr)
Sicherheitslücken (CVE)Smart Contract Vulnerability Surface Analysis: HashKey Exchange(24.09.2026 um 11:02 Uhr)
Sichere ProgrammierungAI Agents Calling Your Existing Backend Without MCP Development(24.09.2026 um 11:06 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Why I Stopped Using MediatR and Built CQRS From Scratch in .NET 10 published: false

Why I Stopped Using MediatR and Built CQRS From Scratch in .NET 10 MediatR is a great library. I'm not here to trash it. But when it moved to a paid model for commercial use, a lot of teams — including mine — had to make a decision. And …

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!




Why I Stopped Using MediatR and Built CQRS From Scratch in .NET 10



MediatR is a great library. I'm not here to trash it.



But when it moved to a paid model for commercial use, a lot of teams — including mine — had to make a decision. And the more I looked at what MediatR actually does under the hood, the more I realised: this is not complicated enough to justify an external dependency.



So I built it myself. Here's what that looks like, why I made each decision, and whether it was worth it.









What MediatR actually does



Strip away the marketing and MediatR does two things:




  1. Takes a request object and routes it to the right handler

  2. Runs a configurable pipeline of behaviors before and after the handler



That's it. The "mediator pattern" is mostly a fancy name for a dispatcher with middleware. Understanding this is the unlock — once you see it that way, building it yourself feels obvious.









The core abstractions



Everything starts with a request marker interface and its handler:




public interface IRequest<TResponse> { }

public interface IRequestHandler<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken);
}






Then I split commands and queries explicitly. This enforces CQRS at the type level — a command can't accidentally be used where a query is expected:




// Commands mutate state — always return Result or Result<T>
public interface ICommand : IRequest<Result> { }
public interface ICommand<TResponse> : IRequest<Result<TResponse>> { }

// Queries are read-only — always return Result<T>
public interface IQuery<TResponse> : IRequest<Result<TResponse>> { }






And their handlers:




public interface ICommandHandler<TCommand, TResponse>
: IRequestHandler<TCommand, Result<TResponse>>
where TCommand : ICommand<TResponse> { }

public interface IQueryHandler<TQuery, TResponse>
: IRequestHandler<TQuery, Result<TResponse>>
where TQuery : IQuery<TResponse> { }






Notice everything returns Result<T> — I'll come back to that.









The Dispatcher



This is the piece that does the actual routing. It resolves the handler from DI and chains pipeline behaviors around it:




internal sealed class Dispatcher(IServiceProvider serviceProvider) : IDispatcher
{
public async Task<TResponse> Send<TResponse>(
IRequest<TResponse> request,
CancellationToken cancellationToken)
{
var requestType = request.GetType();
var handlerType = typeof(IRequestHandler<,>)
.MakeGenericType(requestType, typeof(TResponse));

var handler = serviceProvider.GetRequiredService(handlerType);

var behaviors = serviceProvider
.GetServices(typeof(IPipelineBehavior<,>)
.MakeGenericType(requestType, typeof(TResponse)))
.Cast<dynamic>()
.Reverse()
.ToList();

RequestHandlerDelegate<TResponse> pipeline = ct =>
{
var method = handlerType.GetMethod(nameof(IRequestHandler<IRequest<TResponse>, TResponse>.Handle))!;
return (Task<TResponse>)method.Invoke(handler, [request, ct])!;
};

pipeline = behaviors.Aggregate(pipeline, (next, behavior) =>
ct => behavior.Handle((dynamic)request, next, ct));

return await pipeline(cancellationToken);
}
}






The behaviors are registered in order in DI. Each one wraps the next, forming a chain. The innermost call is always the handler itself. This is the same mental model as ASP.NET Core middleware — if you understand that, you understand this.









The pipeline



Pipeline behaviors implement a single interface:




public delegate Task<TResponse> RequestHandlerDelegate<TResponse>(CancellationToken cancellationToken);

public interface IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken);
}






I have two behaviors registered: ValidationBehavior (runs first) and LoggingBehavior (wraps everything). Adding a new cross-cutting concern — say, caching or timing — is one class and one line in DependencyInjection.cs. No library update required.



The pipeline order in DI registration:




ValidationBehavior → LoggingBehavior → Handler












Why Result instead of exceptions



This is a separate decision but it compounds well with CQRS. Every handler returns Result or Result<T>. Expected business failures — email already in use, entity not found — are values, not exceptions.




public async Task<Result<Guid>> Handle(
RegisterClientCommand request,
CancellationToken cancellationToken)
{
var emailExists = await repository.ExistsByEmailAsync(request.Email, cancellationToken);
if (emailExists)
return Result.Failure<Guid>(new ConflictError("Client.EmailInUse", "Email is already in use."));

var client = Client.Create(request.FirstName, request.LastName, request.Email);
await repository.InsertAsync(client, cancellationToken);

return Result.Success(client.Id);
}






The controller never needs a try/catch. It maps the result to HTTP via an extension method:




[HttpPost]
public async Task<IActionResult> Register(
[FromBody] RegisterClientCommand command,
CancellationToken cancellationToken)
{
var result = await dispatcher.Send(command, cancellationToken);

return result.IsFailure
? result.ToActionResult()
: CreatedAtAction(nameof(GetById), new { id = result.Value }, null);
}






ToActionResult() maps DomainError.ErrorType to HTTP status codes in one place. NotFoundError → 404, ConflictError → 409, ValidationError → 422. The controller doesn't know about any of this — it just unwraps the result.









DI registration — zero ceremony



One of MediatR's conveniences is auto-registering handlers via assembly scanning. I replicated this in about 15 lines:




private static void RegisterHandlers(IServiceCollection services, Assembly assembly, Type openHandlerType)
{
var handlers = assembly.GetTypes()
.Where(t => t is { IsAbstract: false, IsInterface: false })
.SelectMany(t => t.GetInterfaces(), (impl, iface) => (impl, iface))
.Where(x => x.iface.IsGenericType &&
x.iface.GetGenericTypeDefinition() == openHandlerType);

foreach (var (impl, iface) in handlers)
{
services.AddScoped(iface, impl);

var requestHandlerType = iface.GetInterfaces()
.FirstOrDefault(i => i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IRequestHandler<,>));

if (requestHandlerType is not null)
services.AddScoped(requestHandlerType, impl);
}
}






Adding a new feature — a new command or query — requires zero changes to this registration. Write the handler, implement the interface, done.









Was it worth it?



Honestly, yes — but with an asterisk.



What you gain:




  • No licensing concerns, ever

  • Full visibility into how dispatch and pipeline work

  • Easier to debug — the stack trace is your code, not a library's internals

  • You can extend it in ways MediatR doesn't support without hacks



What you give up:




  • MediatR has years of edge cases handled

  • The ecosystem assumes MediatR (blog posts, templates, other libraries)

  • New team members have to learn your implementation



If you're starting a greenfield project with a stable team, building it yourself is a reasonable call. If you're onboarding contractors every quarter, the familiarity of MediatR has real value.



For me, migrating a production system and wanting full control over every layer — it was the right decision.









The full implementation



Everything in this article is part of a working .NET 10 template I published on GitHub, including Clean Architecture layers, EF Core + Dapper, FluentValidation pipeline, and a full test suite (unit, integration, E2E with Testcontainers).



github.com/ferras991/dotnet-clean-arch-cqrs



If you've been reaching for MediatR out of habit, it might be worth asking whether you actually need it.

SOC Incident Playbook: Remote Code Execution (RCE) Defense
title: Detect Exploitation - Why I Stopped Using MediatR and Built CQRS From Scratch in .NET 10 published: false
id: c9992b8e-adfe-469e-a6bf-f2805c473d73
status: experimental
description: Automatisch generierte SIEM-Erkennungsregel basierend auf CTI Intelligence
references:
  - https://tsecurity.de/
author: iShareStuff CTI Automated Detection Engine
date: 2026-09-24
logsource:
  category: network_connection
  product: any
detection:
  selection:
      CommandLine|contains:
        - 'exploit'
  condition: selection
falsepositives:
  - Legitime administrative Zugriffe oder Penetrationstests
level: high
tags:
  - attack.initial_access
rule CTI_Threat_Indicator {
    meta:
        author = "iShareStuff CTI Automated Detection Engine"
        date = "2026-09-24"
        description = "YARA Signature for "
    strings:
        $str = "Why I Stopped Using MediatR an" ascii wide
    condition:
        any of them
}
tsecurity.de Cognitive Threat RAG
Fokus-Vektor:

Kognitive Analyse für identifizierte Bedrohung: Erhöhte Bedrohungslage im Bereich Why I Stopped Using MediatR and Built CQ.... Basierend auf 368k Vektor-Korrelationen werden sofortige Isolationsmaßnahmen für betroffene Endpunkte empfohlen.

🛡️ Angriffsfläche & Exposure

Netzwerk/Remote-Zugriff ohne Vorauthentifizierung möglich.

Empfohlene Sofortmaßnahmen
  • 1. Perimeter-Inspektion: Relevante Portfreigaben und exponierte Endpunkte unverzüglich scannen.
  • 2. Patch-Applikation: Hersteller-Hotfix einspielen oder betroffene Daemons in isolierte DMZ-Segmente überführen.
  • 3. Telemetrie & EDR-Alerts: Prozessaufrufe und Child-Processes auf anomale Shell-Spawns überwachen.
🔗 Semantisch verwandte Zero-Days MariaDB 11.7 VEC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Why I Stopped Using MediatR and Built CQRS From Scratch in .NET 10 published: false

Thematisch verwandte Begriffe: Stopped, Using, MediatR, Built · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-97056 | SigNoz versions from v0.98.0 up to (but not including) v0.143.0, when co…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel TTP ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick