Use when you have a written implementation plan to execute in a separate session with review checkpoints
npx skills add shinyorg/skills --skill "shiny-mediator"
Install specific skill from multi-skill repository
# Description
Generate Shiny Mediator handlers, contracts, middleware, and scaffold projects for .NET applications
# SKILL.md
name: shiny-mediator
description: Generate Shiny Mediator handlers, contracts, middleware, and scaffold projects for .NET applications
auto_invoke: true
triggers:
- mediator
- handler
- request handler
- command handler
- event handler
- stream handler
- middleware
- IRequest
- ICommand
- IEvent
- CQRS
- Shiny.Mediator
Shiny Mediator Skill
You are an expert in Shiny Mediator, a mediator pattern library for .NET applications.
When to Use This Skill
Invoke this skill when the user wants to:
- Create request handlers, command handlers, event handlers, or stream handlers
- Generate contracts (IRequest, ICommand, IEvent, IStreamRequest)
- Add middleware (caching, resilience, validation, offline)
- Scaffold ASP.NET, MAUI, or Blazor projects with Shiny Mediator
- Configure Shiny Mediator in their application
- Migrate from MediatR to Shiny Mediator
Library Overview
Documentation: https://shinylib.net/mediator
Shiny Mediator is AOT & trimming friendly, using source generators for automatic DI registration.
Core Patterns
| Pattern | Contract | Handler | Usage |
|---|---|---|---|
| Request | IRequest<TResult> |
IRequestHandler<TRequest, TResult> |
Queries returning data |
| Command | ICommand |
ICommandHandler<TCommand> |
Void state changes |
| Event | IEvent |
IEventHandler<TEvent> |
Pub/sub notifications |
| Stream | IStreamRequest<TResult> |
IStreamRequestHandler<TRequest, TResult> |
IAsyncEnumerable |
Handler Registration
Always use registration attributes:
[MediatorSingleton] // Stateless handlers
[MediatorScoped] // Handlers needing per-request services (DbContext)
Critical: Partial Class Requirement
When using any middleware attribute ([Cache], [OfflineAvailable], [Resilient], [MainThread], [TimerRefresh]), the handler class must be declared as partial:
[MediatorSingleton]
public partial class MyHandler : IRequestHandler<MyRequest, MyResult> // partial required!
{
[Cache(AbsoluteExpirationSeconds = 60)]
public Task<MyResult> Handle(...) { }
}
This enables the source generator to create the IHandlerAttributeMarker implementation. Without partial, you'll get error SHINY001.
Basic Setup
ASP.NET:
builder.Services.AddShinyMediator(x => x
.AddMediatorRegistry()
);
app.MapGeneratedMediatorEndpoints();
MAUI:
builder.AddShinyMediator(x => x
.AddMediatorRegistry()
.UseMaui()
.AddMauiPersistentCache()
.PreventEventExceptions()
);
Blazor:
builder.Services.AddShinyMediator(x => x
.AddMediatorRegistry()
.UseBlazor()
.PreventEventExceptions()
);
Code Generation Instructions
When generating Shiny Mediator code:
1. Contracts
Always use records for immutability:
public record GetUserRequest(int UserId) : IRequest<UserDto>;
public record CreateUserCommand(string Name, string Email) : ICommand;
public record UserCreatedEvent(int UserId, string Name) : IEvent;
2. Handlers
Include all three parameters in Handle method:
[MediatorScoped]
public class GetUserRequestHandler : IRequestHandler<GetUserRequest, UserDto>
{
public Task<UserDto> Handle(
GetUserRequest request,
IMediatorContext context,
CancellationToken cancellationToken)
{
// Implementation
}
}
3. Middleware Attributes
Apply to handler methods as needed:
- [Cache(AbsoluteExpirationSeconds = N)] - Cacheable queries
- [OfflineAvailable] - Offline storage for mobile
- [Resilient("policyName")] - Retry/timeout policies
- [MainThread] - MAUI main thread execution
- [TimerRefresh(milliseconds)] - Auto-refresh streams
- [Validate] - Data annotation validation
When using ANY of these attributes, the handler class MUST be partial:
[MediatorSingleton]
public partial class CachedHandler : IRequestHandler<MyRequest, MyData>
{
[Cache(AbsoluteExpirationSeconds = 60)]
[OfflineAvailable]
public Task<MyData> Handle(...) { }
}
4. File Organization
Place files in appropriate folders:
- Contracts: Contracts/{Name}Request.cs, Contracts/{Name}Command.cs
- Handlers: Handlers/{Name}Handler.cs
- Middleware: Middleware/{Name}Middleware.cs
Usage Examples
Request:
var response = await mediator.Request(new GetUserRequest(1));
var user = response.Result;
Command:
await mediator.Send(new CreateUserCommand("John", "[email protected]"));
Event:
await mediator.Publish(new UserCreatedEvent(1, "John"));
Chaining via Context:
public async Task<UserDto> Handle(GetUserRequest request, IMediatorContext context, CancellationToken ct)
{
// Use context to chain operations (shares scope)
await context.Publish(new UserAccessedEvent(request.UserId));
return new UserDto(...);
}
Best Practices
- Use records for contracts - Immutable, value equality
- One handler per file - Focused and testable
- Appropriate lifetime - Singleton for stateless, Scoped for DbContext
- Use
partialwith middleware attributes - Required for[Cache],[OfflineAvailable],[Resilient], etc. - Chain via context - Use
context.Request()not injecting IMediator - Implement IContractKey - For custom cache/offline keys
- Always pass CancellationToken - Respect cancellation
Reference Files
For detailed templates and examples, see:
- reference/templates.md - Code generation templates
- reference/scaffolding.md - Project structure templates
- reference/middleware.md - Middleware configuration
- reference/api-reference.md - Full API and NuGet packages
Common Packages
dotnet add package Shiny.Mediator # Core
dotnet add package Shiny.Mediator.Maui # MAUI
dotnet add package Shiny.Mediator.Blazor # Blazor
dotnet add package Shiny.Mediator.AspNet # ASP.NET
dotnet add package Shiny.Mediator.Resilience # Polly
dotnet add package Shiny.Mediator.Caching.MicrosoftMemoryCache # Caching
dotnet add package Shiny.Mediator.AppSupport # Offline
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.