MediatR IRequest, IRequestHandler, and INotificationHandler patterns. Command and query handler creation, registration, and dispatch. Trigger: MediatR, IRequest, IRequestHandler, handler, Send.
From dotnet-ai-kitnpx claudepluginhub faysilalshareef/dotnet-ai-kit --plugin dotnet-ai-kitThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
IRequest<TResponse> for operations that change stateIRequest<TResponse> for read-only data retrievalINotification for one-to-many event dispatchRegisterServicesFromAssembly// Command record — immutable, represents intent
public sealed record CreateOrderCommand(
string CustomerName,
List<CreateOrderCommand.ItemDto> Items) : IRequest<Result<Guid>>
{
public sealed record ItemDto(Guid ProductId, int Quantity);
}
// Handler — internal, one per command
internal sealed class CreateOrderCommandHandler(
IOrderRepository repository,
IUnitOfWork unitOfWork,
ILogger<CreateOrderCommandHandler> logger)
: IRequestHandler<CreateOrderCommand, Result<Guid>>
{
public async Task<Result<Guid>> Handle(
CreateOrderCommand request, CancellationToken ct)
{
var order = Order.Create(request.CustomerName);
foreach (var item in request.Items)
order.AddItem(item.ProductId, item.Quantity);
repository.Add(order);
await unitOfWork.SaveChangesAsync(ct);
logger.LogInformation(
"Created order {OrderId} for {Customer}",
order.Id, request.CustomerName);
return Result<Guid>.Success(order.Id);
}
}
// Query record
public sealed record GetOrderQuery(Guid OrderId)
: IRequest<OrderResponse?>;
// Handler with DbContext (read-only, no repository needed)
internal sealed class GetOrderQueryHandler(AppDbContext db)
: IRequestHandler<GetOrderQuery, OrderResponse?>
{
public async Task<OrderResponse?> Handle(
GetOrderQuery request, CancellationToken ct)
{
return await db.Orders
.AsNoTracking()
.Where(o => o.Id == request.OrderId)
.Select(o => new OrderResponse(
o.Id,
o.CustomerName,
o.Total,
o.Status.ToString(),
o.CreatedAt,
o.Items.Select(i => new OrderItemResponse(
i.ProductId, i.ProductName,
i.Quantity, i.UnitPrice)).ToList()))
.FirstOrDefaultAsync(ct);
}
}
// IRequest with no generic parameter = Unit return
public sealed record DeleteOrderCommand(Guid OrderId) : IRequest;
internal sealed class DeleteOrderCommandHandler(
IOrderRepository repository,
IUnitOfWork unitOfWork)
: IRequestHandler<DeleteOrderCommand>
{
public async Task Handle(
DeleteOrderCommand request, CancellationToken ct)
{
var order = await repository.FindAsync(request.OrderId, ct)
?? throw new NotFoundException("Order", request.OrderId);
repository.Remove(order);
await unitOfWork.SaveChangesAsync(ct);
}
}
// Notification — dispatched to multiple handlers
public sealed record OrderCreatedNotification(
Guid OrderId,
string CustomerName) : INotification;
// Handler 1: send email
internal sealed class SendOrderConfirmationEmail(
IEmailService emailService)
: INotificationHandler<OrderCreatedNotification>
{
public async Task Handle(
OrderCreatedNotification notification,
CancellationToken ct)
{
await emailService.SendOrderConfirmationAsync(
notification.OrderId, notification.CustomerName, ct);
}
}
// Handler 2: update analytics
internal sealed class UpdateOrderAnalytics(AppDbContext db)
: INotificationHandler<OrderCreatedNotification>
{
public async Task Handle(
OrderCreatedNotification notification,
CancellationToken ct)
{
var stats = await db.DashboardStats.SingleAsync(ct);
stats.IncrementOrderCount();
await db.SaveChangesAsync(ct);
}
}
// Program.cs — register all handlers from assembly
builder.Services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
// Or from multiple assemblies
cfg.RegisterServicesFromAssemblies(
typeof(CreateOrderCommand).Assembly,
typeof(OrderCreatedNotification).Assembly);
});
// ISender for commands and queries
app.MapPost("/orders", async (
CreateOrderCommand cmd,
ISender sender,
CancellationToken ct) =>
{
var result = await sender.Send(cmd, ct);
return result.Match(
id => Results.Created($"/orders/{id}", new { id }),
error => Results.BadRequest(error.ToProblemDetails()));
});
// IPublisher for notifications
app.MapPost("/orders/{id}/submit", async (
Guid id, ISender sender, IPublisher publisher,
CancellationToken ct) =>
{
await sender.Send(new SubmitOrderCommand(id), ct);
await publisher.Publish(
new OrderSubmittedNotification(id), ct);
return Results.NoContent();
});
| Type | Pattern | Example |
|---|---|---|
| Command | {Verb}{Noun}Command | CreateOrderCommand |
| Query | Get{Noun}Query / List{Noun}Query | GetOrderQuery |
| Handler | {Command/Query}Handler | CreateOrderCommandHandler |
| Notification | {Noun}{PastVerb}Notification | OrderCreatedNotification |
| Validator | {Command}Validator | CreateOrderCommandValidator |
internal sealed)IRequest< and IRequestHandler< implementationsINotification and INotificationHandler< implementationsMediatR package reference in .csprojISender or IMediator injectionRegisterServicesFromAssembly callsdotnet add package MediatRAddMediatR in Program.csISender.Send