FuncPipeline 1.0.2
dotnet add package FuncPipeline --version 1.0.2
NuGet\Install-Package FuncPipeline -Version 1.0.2
<PackageReference Include="FuncPipeline" Version="1.0.2" />
<PackageVersion Include="FuncPipeline" Version="1.0.2" />
<PackageReference Include="FuncPipeline" />
paket add FuncPipeline --version 1.0.2
#r "nuget: FuncPipeline, 1.0.2"
#:package FuncPipeline@1.0.2
#addin nuget:?package=FuncPipeline&version=1.0.2
#tool nuget:?package=FuncPipeline&version=1.0.2
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.ServiceScopeFactoryproperty.
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 passingPipelineRunOptionstoPipelineBuilder.NewPipeline(PipelineRunOptions? options = null).
The default isPipelineRunOptions.ServiceScopePerFunction = true. - PipelineRunContext: Using the
PipelineRunContextinstance passed toIPipeline.RunAsync().
Parameters always available:
INextinstance 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.ThrowExceptionorNotFoundBehavior.ReturnTypeDefault(default). - SecondaryNotFound: Behavior when the parameter is not found in the secondary source.
NotFoundBehavior.ThrowExceptionorNotFoundBehavior.ReturnTypeDefault(default). - Key: An optional key for resolving the parameter, for keyed
IServiceProviderregistrations.
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 is compatible. 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. |
-
net10.0
-
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.