Skip to main content

Running Pipelines

Once your pipeline steps are discovered and registered, you can run the pipeline by resolving and invoking an IPipelineRunner<T> from the dependency injection container.

Each IPipelineRunner<T> executes the pipeline steps for a specific context type T, in the order defined by their [PipelineStep] attribute.

Example Usage

Executing a Pipeline
[PipelineStep(1)]
public class StepA : PipelineStep<StepContext>
{
public override async Task InvokeAsync(StepContext context, PipelineDelegate<StepContext> next, CancellationToken cancellationToken = default)
{
context.AddStep("A1");
await next(context, cancellationToken);
}
}

[PipelineStep(2)]
public class StepB : PipelineStep<StepContext>
{
public override async Task InvokeAsync(StepContext context, PipelineDelegate<StepContext> next, CancellationToken cancellationToken = default)
{
context.AddStep("B2");
await next(context, cancellationToken);
}
}

[PipelineStep(3)]
public class StepC : PipelineStep<StepContext>
{
public override async Task InvokeAsync(StepContext context, PipelineDelegate<StepContext> next, CancellationToken cancellationToken = default)
{
context.AddStep("C3");
await next(context, cancellationToken);
}
}

var context = new SampleContext();
var services = new ServiceCollection();
services.AddPipelineFor<SampleContext>();

var provider = services.BuildServiceProvider();
var runner = provider.GetRequiredService<IPipelineRunner<SampleContext>>();

await runner.ExecuteAsync(context);

Console.WriteLine(context.ToString()); // e.g. "A1,B2,C3"

Each registered pipeline step for SampleContext will be executed in order, unless short-circuited by a step that chooses not to call next().

Behavior Details

  • The pipeline is lazily instantiated. Only the steps needed for the current run will be resolved from the container.
  • If a step short-circuits (by not calling next()), remaining steps will not be resolved or executed.
  • Exceptions thrown in any step will bubble up unless explicitly handled inside the step.
  • Unhandled exceptions during pipeline execution will be wrapped in a PipelineExecutionException<T>, which preserves the original exception as an inner exception.

Stateless Runners

Pipeline runners are stateless and safe to reuse. You can execute the same runner multiple times with different contexts:

var first = new SampleContext();
await runner.ExecuteAsync(first);

var second = new SampleContext();
await runner.ExecuteAsync(second);

Conclusion

Your pipeline is now fully operational. In the next section, you'll learn how to test pipelines and steps independently.