PipelineMedium 1.0.2
dotnet add package PipelineMedium --version 1.0.2
NuGet\Install-Package PipelineMedium -Version 1.0.2
<PackageReference Include="PipelineMedium" Version="1.0.2" />
<PackageVersion Include="PipelineMedium" Version="1.0.2" />
<PackageReference Include="PipelineMedium" />
paket add PipelineMedium --version 1.0.2
#r "nuget: PipelineMedium, 1.0.2"
#addin nuget:?package=PipelineMedium&version=1.0.2
#tool nuget:?package=PipelineMedium&version=1.0.2
PipelineMedium is set of classes and interfaces that provides a flexible and extensible solution of middleware pipeline pattern.
Installing
You can install it with NuGet:
dotnet add package PipelineMedium
Concepts
Middleware
Middleware is piece of software that's assembled into a pipeline to handle data and produce result. Each component can choose whether to run the next component in the pipeline and perform work before and after the next component.
//example of middleware function
(payload, next) =>
{
//run before next middleware
next(); //invoke next middleware in pipeline
//run after next middleware
//or
return; //break execution of middleware pipeline (short-circuiting)
}
Middleware components can be of two types: consumable that can only read or modify a payload and produceable that also returns a result. An individual middleware component can be specified in-line as an anonymous method or it can be defined in a reusable class. Middleware class supports dependency injection via constructor. Default lifetime of middleware class instance is transient, to change lifetime register a middleware in services collection on your own.
//produceable middleware class
public class Middleware(IDependency dependency) : IMiddleware<Payload, Result>
{
public Result Invoke(Payload payload, NextMiddlewareDelegate<Result> next)
{
//code
}
}
//produceable async middleware class
public class AsyncMiddleware(IDependency dependency) : IAsyncMiddleware<Payload, Result>
{
public async Task<Result> InvokeAsync(Payload payload, NextAsyncMiddlewareDelegate<Result> next)
{
//code
}
}
//consumable middleware class
public class Middleware(IDependency dependency) : IMiddleware<Payload>
{
public void Invoke(Payload payload, NextMiddlewareDelegate next)
{
//code
}
}
//consumable async middleware class
public class AsyncMiddleware(IDependency dependency) : IAsyncMiddleware<Payload>
{
public async Task InvokeAsync(Payload payload, NextAsyncMiddlewareDelegate next)
{
//code
}
}
//in-line middleware functions
services.AddMedium<Payload>()
.Use((payload, next) => { /* code */ })
.Use((serviceProvider, payload, next) => { /* code */ }) //with IServiceProvider
.Use<Dependency>((dependency, payload, next) => { /* code */ }); //with Dependency provided
services.AddMedium<Payload, Result>()
.Use((payload, next) => { /* code */ })
.Use((serviceProvider, payload, next) => { /* code */ }) //with IServiceProvider
.Use<Dependency>((dependency, payload, next) => { /* code */ }); //with Dependency provided
Medium
Medium is definition of middleware pipeline. It encapsulates functionality of building, managing a pipeline and provides convenient interface to execute the pipeline.
To define a medium add it to IserviceCollection
and configure its middleware pipeline
//add default consumable Medium
services.AddMedium<Payload>()
.Use<Middleware>()
.Use<NextMiddleware>()
.UseWhen<ConditionalMiddleware>(p => p.Param == null)
.Use((payload, next) => { /* code */ });
//add default produceable Medium
services.AddMedium<Payload, Result>()
.Use<Middleware>()
.Use<NextMiddleware>()
.UseWhen<ConditionalMiddleware>(p => p.Param == null)
.Use((payload, next) => { /* code */ });
//you can also add other mediums for the same payload, result type by specifying a name (default name is "Default")
//name uniquely identifies a medium for specified payload type
services.AddMedium<Payload>("DoDifferentWay")
.Use<OtherMiddleware>()
//name uniquely identifies a medium for specified payload and result types combination
services.AddMedium<Payload, OtherResult>("DoDifferentWayAndReturnDifferentResult")
.Use<OtherMiddleware>()
To execute a Medium get an instance from service provider by its generic interface IMedium<TPayload>
, IMedium<TPayload, TResult>
from service provider.
Also IMedium
interface available with generic methods for convenient access to different mediums.
var medium = serviceProvider.GetRequiredService<IMedium<Payload>>();
await medium.ExecuteAsync(new Payload());
await medium.ExecuteAsync("DoDifferentWay", new Payload());
var medium = serviceProvider.GetRequiredService<IMedium<Payload, Result>>();
var result = await medium.ExecuteAsync(new Payload());
var result2 = await medium.ExecuteAsync("DoDifferentWayAndReturnDifferentResult", new Payload());
var medium = serviceProvider.GetRequiredService<IMedium>();
await medium.ExecuteAsync<Payload>(new Payload());
await medium.ExecuteAsync<OtherPayload>(new OtherPayload());
Usage
Define a payload type
public class Payload {
public string Param { get; set; }
}
Define a result type if you want medium to return a result
public class Result {
public string Data { get; set; }
}
Create a middleware to handle the payload and to return result if desired. Middleware class must implement middleware interface.
//consumable middleware
public class ConsumePayloadMiddleware(IDependency dependency) : IAsyncMiddleware<Payload>
{
public async Task InvokeAsync(Payload payload, NextAsyncMiddlewareDelegate next)
{
//code
await next(); //invoke next middleware in pipeline
}
}
//produceable middleware
public class ConsumePayloadAndReturnResultMiddleware(IDependency dependency) : IAsyncMiddleware<Payload, Result>
{
public async Task<Result> InvokeAsync(Payload payload, NextAsyncMiddlewareDelegate<Result> next)
{
//code
return await next(); //invoke next middleware in pipeline
}
}
Add medium to IserviceCollection
and configure its middleware pipeline.
//add and configure default medium for processing the Payload
services.AddMedium<Payload>()
.Use<PrecedingMiddleware>()
.Use<ConsumePayloadMiddleware>()
.Use<NextMiddleware>()
.UseWhen<ConditionalMiddleware>(p => p.Param == null)
.Use((p, next) => { });
//add and configure default medium for processing the Payload and return the Result
services.AddMedium<Payload, Result>()
.Use<PrecedingMiddleware>()
.Use<ConsumePayloadAndReturnResultMiddleware>()
.Use<NextMiddleware>()
.UseWhen<ConditionalMiddleware>(p => p.Param == null)
.Use((p, next) => { });
Get an instance of medium by its interface using dependency injection
public class SomeController : ControllerBase
{
public async Task<ActionResult<Result>> Get(Payload requestModel, [FromServices] IMedium<Payload, Result> medium)
{
return await medium.ExecuteAsync(requestModel);
}
public async Task<IActionResult> Post(Payload requestModel, [FromServices] IMedium<Payload> medium)
{
await medium.ExecuteAsync(requestModel);
return Ok();
}
public async Task<IActionResult> Put(Payload requestModel, [FromServices] IMedium medium)
{
await medium.ExecuteAsync<Payload>(requestModel);
await medium.ExecuteAsync<OtherPayload>(new OtherPayload());
return Ok();
}
}
Features
Syncronous and asyncronous middleware in one pipeline
services.AddMedium<Payload>()
.Use<SyncMiddleware>()
.Use<AsyncMiddleware>()
.Use((p, next) => { })
.Use(async (p, next, cancellationToken) => { });
Default or terminate component of a pipeline. When execution of the pipeline goes next the way down, Medium executes a terminate component to end the process and return a result, you can define own behavior of that component.
services.AddMedium<Payload>()
.Use<SomeMiddleware>()
.Use((p, next) => { next() })
.SetDefault(p => {
Console.WriteLine("Execution ended");
});
services.AddMedium<Payload, Result>()
.Use<SomeMiddleware>()
.Use((p, next) => { next() })
.SetDefault(p => {
return new Result();
});
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 is compatible. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 3.1.0)
- Microsoft.Extensions.Options (>= 3.1.0)
-
net5.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0.0)
- Microsoft.Extensions.Options (>= 5.0.0)
-
net7.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 7.0.0)
- Microsoft.Extensions.Options (>= 7.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.