Pipeline Behaviors
Add cross-cutting logic with behaviors.
Pipeline Behaviors is a free C# Academy lesson on CoddyKit. This is lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C# Academy learning path, and your progress syncs across the web and the CoddyKit app. The C# Academy course includes 4 lessons in total.
Cross-Cutting Concerns
Logging, validation, timing and transactions apply to many handlers. Copying that code into each handler is repetitive and error-prone.
MediatR's pipeline behaviors let you wrap every request with shared logic.
// One behavior runs around all handlersIPipelineBehavior
A behavior implements IPipelineBehavior<TRequest, TResponse>. Its Handle method receives the request and a next delegate that invokes the rest of the pipeline.
public class LoggingBehavior<TRequest, TResponse>
: IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull
{
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken ct)
{
// before
var response = await next();
// after
return response;
}
}The next Delegate
next() calls the next behavior, or the actual handler if this is the last one. Code before the call runs on the way in; code after runs on the way out.
// [Behavior A in] -> [Behavior B in] -> [Handler]
// <- [Behavior B out] <- [A out]A Logging Behavior
A concrete logging behavior records the request name on the way in and the elapsed time on the way out.
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken ct)
{
var name = typeof(TRequest).Name;
_logger.LogInformation("Handling {Name}", name);
var sw = Stopwatch.StartNew();
var response = await next();
_logger.LogInformation("{Name} took {Ms}ms", name, sw.ElapsedMilliseconds);
return response;
}Registering a Behavior
Register the open generic behavior in DI. MediatR applies it to every request.
builder.Services.AddTransient(
typeof(IPipelineBehavior<,>),
typeof(LoggingBehavior<,>));A Validation Behavior
A popular use is request validation with FluentValidation. The behavior runs all validators before the handler executes, throwing if any fail.
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken ct)
{
var context = new ValidationContext<TRequest>(request);
var failures = _validators
.Select(v => v.Validate(context))
.SelectMany(r => r.Errors)
.Where(f => f is not null)
.ToList();
if (failures.Count != 0)
throw new ValidationException(failures);
return await next();
}Short-Circuiting
A behavior can skip the handler entirely by returning without calling next() - useful for caching query results or rejecting invalid requests.
if (_cache.TryGet(key, out TResponse cached))
return cached; // handler never runs
var response = await next();
_cache.Set(key, response);
return response;Ordering Behaviors
Behaviors execute in the order they are registered. Put logging outermost, then validation, then transactions, so each wraps the next correctly.
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(TransactionBehavior<,>));A Transaction Behavior
Wrap command handlers in a database transaction so the whole operation commits or rolls back atomically.
await using var tx = await _db.Database.BeginTransactionAsync(ct);
try
{
var response = await next();
await tx.CommitAsync(ct);
return response;
}
catch
{
await tx.RollbackAsync(ct);
throw;
}Targeting Specific Requests
Use a marker interface and a generic constraint so a behavior only applies to requests that opt in - for example, only commands get transactions.
public interface ITransactional { }
public record CreateOrderCommand(...) : IRequest<int>, ITransactional;
// where TRequest : ITransactionalWhy Behaviors Beat Middleware
Unlike HTTP middleware, behaviors run per message, have the typed request and response, and work even when a request is dispatched outside of HTTP (e.g. from a background job).
// Strongly typed, transport-independent cross-cutting logicQuick Check
Confirm how behaviors work.
Recap
You learned pipeline behaviors:
IPipelineBehavior<TRequest, TResponse>wraps every request with shared logic.- The
nextdelegate advances the pipeline; skipping it short-circuits the handler. - Common uses: logging, validation, transactions, caching.
- Behaviors run in registration order and are strongly typed.
Next: notifications and events.
Learn C# with an AI tutor — free
Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.
- Courses
- 93
- Lessons
- 346
Frequently Asked Questions
Is the “Pipeline Behaviors” lesson free?
Yes — the full text of “Pipeline Behaviors” is free to read here on the web. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C# Academy course, upgrade to CoddyKit PRO. The C# Academy course includes 4 lessons in total.
What will I learn in “Pipeline Behaviors”?
Add cross-cutting logic with behaviors. You practise C# Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start C# Academy?
No prior experience is required. C# Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 3 of 4.
How long does the “Pipeline Behaviors” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this C# Academy lesson?
Yes. Every C# Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- CQRS Concepts
- Commands and Handlers with MediatR
- Pipeline Behaviors
- Notifications and Events