Pipelines is a lightweight library designed to simplify the implementation of the mediator pattern.
Isntall the latest package using the method that works for you as provided by the nuget site.
The package includes everything you need, including the generator.
- Request and Response: Define your requests and responses.
- Handlers: Create handlers for your request/response pairs.
- Execution: Use the
Pipelineclass as a mediator to find the appropriate pipeline or inject/create aRequestPipeline, then execute the pipeline for a given request. This executes any behavrios before executing the handler and returns the expected response. - Behaviors: Add behaviors to your pipelines to customize the processing of requests and responses. This allows for additional functionality, such as logging, validation, or performance monitoring. Behavrios can be added to the DI too and will be injected.
- Source Generation: The
Pipelines.Generatorsimplifies the development process by generating code for handler registration, pipeline creation, and DI integration.
Note
Implementing a specific interface for requests is optional, but it helps when using Pipeline class as you don't have to provide the generic arguments.
Note
Generated handler registration registers handlers on their interfaces. Requesting an IRequestHandler<object, object> will give you your handler but requesting a RequestPipeline<object, object> will give you a built pipeline with your handler wrapped by any behaviors that could be injected.
You can also find samples here.
Declaring request/response/handler:
public class MyRequest : IRequest<MyRequest, MyResponse> // interface here is optional
{
public string Data { get; set; } = string.Empty;
}
public class MyResponse
{
public string Result { get; set; } = string.Empty;
}
public class MyHandler : IRequestHandler<MyRequest, MyResponse>
{
public ValueTask<MyResponse> Handle(MyRequest request)
{
return new(new MyResponse { Result = $"Processed: {request.Data}" });
}
}DI registration if using Microsoft.Extensions.DependencyInjection
services.AddPipelines(); // adds pipelines to the di
services.AddHandlers(); // adds the assembly handlers to the diExecuting a request from di:
var pipeline = serviceProvider.GetRequiredService<Pipeline>();
// with interface
var response1 = pipeline.Request(new MyRequest { Data = "Hello, World!" });
// without interface
var response2 = pipeline.Request<MyRequest, MyResponse>(new MyRequest { Data = "Hello, World!" });Declaring stream request/response/handler:
public class MyStreamRequest : IStreamRequest<MyStreamRequest, string> // interface here is optional
{
public string Query { get; set; }
}
public class MyStreamHandler : IStreamRequestHandler<MyStreamRequest, string>
{
public async IAsyncEnumerable<string> Handle(MyStreamRequest request)
{
yield return $"Result 1 for {request.Query}";
yield return $"Result 2 for {request.Query}";
}
}DI registration same as Requests
Executing a stream from di:
var pipeline = serviceProvider.GetRequiredService<Pipeline>();
// with interface
await foreach (var result in pipeline.Stream(new MyStreamRequest { Query = "Test" }))
{
Console.WriteLine(result);
}
// without interface
await foreach (var result in pipeline.Stream<MyStreamRequest, string>(new MyStreamRequest { Query = "Test" }))
{
Console.WriteLine(result);
}To build and test the project you need an editor/ide of your choice and the .NET SDK.
- Pipelines: Provides the main contracts to build and execute a pipeline.
- Pipelines.Generator: Provides per assembly extension method generation for registering pipeline and handler implementations to the DI. Referenced by Pipelines and there is no need to install.
- Root: Contains the
Pipelineclass that acts as a mediator, determining which pipeline to execute for a given request backed by anIServiceProvider. - Requests: Request (Command, Query) functionality and contracts.
- Streams: StreamRequest functionality and contracts.
The project uses TUnit and the code was modeled after Andrew Lock's article Creating a source generator.
- Pipelines.Generator.Test Generator tests using Verify to validated generated output for each possible case. Each test case has it's files in the Resources folder with a
Program.csfile for compilation. it also includes the verify files. The naming convetion is{ClassName}/{MethodName}/Program.cs. - Pipelines.Generator.Test.Integration Tests all pipeline classes and the generated code from the generator by trying to immitate day to day use.
- Pipelines.Generator.Test.Integration.NuGet Executes all integration tests by publishing the package at a local nuget source
All tests can be executed using dotnet (excluding nuget), visual studio (excluding nuget) or better by using cake eg:
dotnet cake.cs -c Release -- --target testdotnet cake.cs -c Release -- --target test-nuget