Skip to content

HereticSoftware/Pipelines

Pipelines

License .NET Standard 2.0 .NET 8 .NET 9 .NET 10

NuGet Version NuGet Version Downloads

Publish

Pipelines is a lightweight library designed to simplify the implementation of the mediator pattern.

Installation

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.

How It Works

  1. Request and Response: Define your requests and responses.
  2. Handlers: Create handlers for your request/response pairs.
  3. Execution: Use the Pipeline class as a mediator to find the appropriate pipeline or inject/create a RequestPipeline, then execute the pipeline for a given request. This executes any behavrios before executing the handler and returns the expected response.
  4. 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.
  5. Source Generation: The Pipelines.Generator simplifies 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.

Examples

You can also find samples here.

Requests

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 di

Executing 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!" });

Streams

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);
}

Build and Test

To build and test the project you need an editor/ide of your choice and the .NET SDK.

Structure

Projects

  • 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.

Features

  • Root: Contains the Pipeline class that acts as a mediator, determining which pipeline to execute for a given request backed by an IServiceProvider.
  • Requests: Request (Command, Query) functionality and contracts.
  • Streams: StreamRequest functionality and contracts.

Testing

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.cs file 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 test
  • dotnet cake.cs -c Release -- --target test-nuget

About

Lightweight library designed to simplify the implementation of the mediator pattern

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

No releases published

Contributors