FuncPipeline 1.0.1
dotnet add package FuncPipeline --version 1.0.1
NuGet\Install-Package FuncPipeline -Version 1.0.1
<PackageReference Include="FuncPipeline" Version="1.0.1" />
<PackageVersion Include="FuncPipeline" Version="1.0.1" />
<PackageReference Include="FuncPipeline" />
paket add FuncPipeline --version 1.0.1
#r "nuget: FuncPipeline, 1.0.1"
#addin nuget:?package=FuncPipeline&version=1.0.1
#tool nuget:?package=FuncPipeline&version=1.0.1
FuncPipeline
FuncPipeline is a library for building and executing function pipelines using nested function composition. Supports dependency injection, custom parameter resolution, and asynchronous execution.
Building Pipelines
Pipelines are created with a fluent API using the PipelineBuilder
class.
Each Execute
call defines a function in the pipeline.
Functions will be executed in the order of definition. INext.RunAsync()
defines the call to the next function in the pipeline.
Example:
var pipelineBuilder = pipelineBuilder.Create()
.NewPipeline()
.Execute(async (INext next) =>
{
Console.WriteLine("Before A");
await next.RunAsync();
Console.WriteLine("After A");
})
.Execute(async () => Console.WriteLine("A"))
.Build();
// When run, this pipeline will produce the output:
// Before A
// A
// After A
PipelineBuilder
can create multiple pipelines by using NewPipeline()
after Build()
.
PipelineBuilder.Pipelines
exposes created pipelines in the order of their definition.
PipelineBuilder.Build(Action<IPipeline>? onBuild = null)
accepts an optional callback invoked with the currently built pipeline.
Executing Pipelines
Once a pipeline is built, it is executed with the RunAsync
method of the IPipeline
interface. The pipeline optionally accepts a PipelineRunContext
and a CancellationToken
.
Example:
PipelineRunResult result = await pipeline.RunAsync();
IPipeline.RunAsync()
does not throw exceptions.
PipelineRunResult.IsSuccessful
indicates whether the pipeline completed without exceptions. If there was an exception, it is accessible through PipelineRunResult.Exception
.
PipelineRunResult.Context
exposes the PipelineRunContext
instance used in the run.
Parameter Resolution
FuncPipeline supports function parameter resolution from:
- IServiceProvider: Using the service provider either passed in
PipelineBuilder.Create(IServiceScopeFactory? serviceScopeFactory = null)
or set to theIPipeline.ServiceScopeFactory
property.
Function dependencies are always resolved from a newIServiceScope
, disposed of after each run. Functions can share a scope, or alternatively, each function uses a separate scope. This is controlled on a pipeline level by passingPipelineRunOptions
toPipelineBuilder.NewPipeline(PipelineRunOptions? options = null)
.
The default isPipelineRunOptions.ServiceScopePerFunction = true
. - PipelineRunContext: Using the
PipelineRunContext
instance passed toIPipeline.RunAsync()
.
Parameters always available:
INext
instance for invoking the next function.PipelineRunContext
- the one passed toIPipeline.RunAsync()
, or an internally created instance for the run.CancellationToken
- the one passed toIPipeline.RunAsync()
ordefault
.
Example:
var pipelineBuilder = pipelineBuilder.Create()
.NewPipeline()
// Parameters always available without any setup
.Execute(async (INext next, PipelineRunContext context, CancellationToken ct) =>
{
context.Set<int>(123);
return next.RunAsync();
})
.Execute(static (PipelineRunContext context) =>
{
if(context.TryGet<int>(out var value))
{
Console.WriteLine($"Int value from PipelineRunContext: {value}");
}
else
{
Console.WriteLine("Int value not found in PipelineRunContext");
}
return Task.CompletedTask;
})
.Build();
The default behavior of parameter resolution follows the order:
- Try to resolve from
IServiceProvider
. - Try to resolve from
PipelineRunContext
. - Return the default value for the parameter type.
Each pipeline run has a PipelineRunContext
. It is passed as a parameter to IPipeline.RunAsync()
, or created internally.
PipelineRunContext
is a wrapper over Dictionary<Type, object>
and can be used to pass parameters that are not services registered in the service provider. It can be used to pass data between functions and is available through PipelineRunResult.Context
when the pipeline run is completed.
Values are stored in the context based on their type. This allows them to be injected as parameters as if they were registered in the service provider.
Example:
serviceCollection.Add<IService, Service>();
...
var pipelineBuilder = pipelineBuilder.Create(serviceScopeFactory)
.NewPipeline()
.Execute(async (
INext next /* always available */,
PipelineRunContext context /* always available */,
IService service /* resolved from IServiceProvider */)
=>
{
int value = service.GetInteger(); // returns 123
context.Set<int>(value);
await next.RunAsync();
})
.Execute(async (
IContextService contextService /* resolved from context */,
int integer /* resolved from context (123)*/ )
=>
{
...
})
.Build();
...
var context = new PipelineRunContext()
.Set<IContextService>(instance);
var result = await pipeline.RunAsync(context);
_ = result.TryGet<int>(out var integer); // integer = 123
Customizing Parameter Resolution Behavior
The ResolveFromAttribute
allows control over how parameters are resolved.
- PrimarySource: The primary source for resolution.
Source.Services
(default) orSource.Context
. - Fallback: Whether to fall back to a secondary source if the primary source fails. Default is
true
. - PrimaryNotFound: Behavior when the parameter is not found in the primary source.
NotFoundBehavior.ThrowException
orNotFoundBehavior.ReturnTypeDefault
(default). - SecondaryNotFound: Behavior when the parameter is not found in the secondary source.
NotFoundBehavior.ThrowException
orNotFoundBehavior.ReturnTypeDefault
(default). - Key: An optional key for resolving the parameter, for keyed
IServiceProvider
registrations.
ResolveFromAttribute
can decorate a function parameter or alternatively can be passed as a parameter when building the pipeline.
Example:
pipelineBuilder
.NewPipeline()
// Change parameter resolution behavior
.Execute(static async (
[ResolveFrom(PrimarySource = Source.Services, Fallback = true, SecondaryNotFound = NotFoundBehavior.ThrowException, Key = "service key")]
IService1 s1,
[ResolveFrom(PrimarySource = Source.Context, Fallback = false, PrimaryNotFound = NotFoundBehavior.ThrowException)]
IService1 s2) => { ... })
// Equivalent behavior definition
.Execute(static async (
IService1 s1,
IService1 s2) => { ... },
new Dictionary<int, ResolveFromAttribute>
{
{
0, // Should match the parameter position (s1)
new ResolveFromAttribute
{
PrimarySource = Source.Services,
Fallback = true,
SecondaryNotFound = NotFoundBehavior.ThrowException,
Key = "service key"
}
},
{
1, // Should match the parameter position (s2)
new ResolveFromAttribute
{
PrimarySource = Source.Context,
Fallback = true,
PrimaryNotFound = NotFoundBehavior.ThrowException
}
}
})
.Build();
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. 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 is compatible. 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. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
-
net9.0
NuGet packages (1)
Showing the top 1 NuGet packages that depend on FuncPipeline:
Package | Downloads |
---|---|
M.EventBrokerSlim
In-memory fire-and-forget event broker. |
GitHub repositories
This package is not used by any popular GitHub repositories.