In this section of the documentation, you'll learn how to build first Pipeline.
The "Input" acts as the initial parameter for Handler and Dispatcher methods, guiding the search for the relevant Handler.
public interface IInput<TResult> where TResult: class{ }
Handlers house the application logic and can generate both synchronous and asynchronous results.
public interface IHandler<in TInput, TResult> where TInput : IInput<TResult> where TResult: class
{
public Task<TResult> HandleAsync(TInput input, CancellationToken token);
}
Serving as a bridge between inputs and their respective handlers, the Dispatcher ensures the appropriate Handler is triggered for a given Input.
public interface IDispatcher
{
public Task<TResult> SendAsync<TResult>(IInput<TResult> input, CancellationToken token) where TResult : class;
}
Analogous to Middlewares in .NET. Think of them as layers of logic that execute before or after the handler.
public class LoggingDecorator<TInput, TResult> : IHandler<TInput, TResult> where TInput : IInput<TResult> where TResult : class
{
private readonly IHandler<TInput, TResult> _handler;
private readonly ILogger _logger;
public LoggingDecorator(IHandler<TInput, TResult> handler, ILogger logger)
{
_handler = handler;
_logger = logger;
}
public async Task<TResult> HandleAsync(TInput request, CancellationToken token)
{
_logger.Log(LogLevel.Information,"Executing handler for input {0}", typeof(TInput));
var result = await _handler.HandleAsync(request, token);
_logger.Log(LogLevel.Information,"Executed handler for input {0}", typeof(TInput));
return result;
}
}
In your application's initialization, such as Startup.cs
:
IMPORTANT! All provided types must be specified using the typeof() method!
var handlersAssembly = //Assembly where handlers assembly are implemented
var dispatcherAssembly = //Assembly where AddPipeline gets invoked
_services
.AddPipeline()
.AddInput(typeof(IInput<>))
.AddHandler(typeof(IHandler<,>), handlersAssembly)
.AddDispatcher<IDispatcher>(dispatcherAssembly)
.WithDecorator(typeof(LoggingDecorator<,>))
.Build();
public record ExampleInput(string Value) : IInput<ExampleCommandResult>;
public record ExampleCommandResult(string Value);
public class ExampleHandler : IHandler<ExampleInput, ExampleCommandResult>
{
public Task<ExampleCommandResult> HandleAsync(ExampleInput input, CancellationToken token)
{
return Task.FromResult(new ExampleCommandResult(input.Value));
}
}
public static void CreateExampleEndpoint(this WebApplication app)
{
app.MapPost("/example", async (ExampleInput request, IDispatcher dispatcher, CancellationToken token) =>
{
var result = await dispatcher.SendAsync(input,token);
return Results.Ok();
});
}
Advanced examples: