Skip to content
This repository was archived by the owner on Jan 31, 2026. It is now read-only.

Latest commit

 

History

History
360 lines (279 loc) · 9.66 KB

File metadata and controls

360 lines (279 loc) · 9.66 KB

Pipeline System

License .NET

A high-performance, composable middleware pipeline system with built-in diagnostics and runtime inspection.

🚀 Features

  • Composable Middleware - Clear, ordered execution of middleware components
  • Async-First Design - Fully async, allocation-aware execution model
  • Conditional Execution - UseWhen for context-based middleware activation
  • Branch Pipelines - UseBranch for isolated sub-pipelines
  • Runtime Inspection - Inspect, remove, or modify middleware at runtime
  • Zero-Cost Diagnostics - Optional debug instrumentation with no overhead when disabled
  • High Performance - No reflection, no dynamic dispatch in hot path

⚡ Quick Start

using Core.Features.Pipeline.Runtime;
using Core.Features.Pipeline.Abstractions.Middleware;

// 1. Define context
public sealed class RequestContext
{
    public string UserId { get; init; }
    public string TenantId { get; init; }
    public IReadOnlyCollection<string> Roles { get; init; }
}

// 2. Create middleware
public class LoggingMiddleware : IMiddleware<RequestContext>
{
    public async Task InvokeAsync(
        RequestContext context,
        Func<Task> next,
        CancellationToken cancellationToken = default)
    {
        Console.WriteLine($"Request started for user: {context.UserId}");
        await next();
        Console.WriteLine($"Request completed");
    }
}

// 3. Build pipeline
var builder = new PipelineBuilder<RequestContext>();
builder.Use(new LoggingMiddleware());
builder.UseWhen(ctx => ctx.Roles.Contains("User"), new ValidationMiddleware());
builder.UseBranch(
    ctx => ctx.TenantId == "tenant-456",
    branch =>
    {
        branch.Use(new AuditMiddleware());
        branch.Use(new FeatureToggleMiddleware());
    }
);

// 4. Execute
var executor = new PipelineExecutor<RequestContext>(builder);
await executor.ExecuteAsync(new RequestContext
{
    UserId = "user-123",
    TenantId = "tenant-456",
    Roles = new[] { "User" }
});

Why Use Pipeline System?

Without Pipeline ❌

public async Task Handle(RequestContext ctx)
{
    await Log(ctx);
    
    if (ctx.Roles.Contains("User"))
        await Validate(ctx);
        
    if (ctx.TenantId == "tenant-456")
    {
        await Audit(ctx);
        await FeatureToggle(ctx);
    }
}

With Pipeline ✅

builder.Use(new LoggingMiddleware());
builder.UseWhen(ctx => ctx.Roles.Contains("User"), new ValidationMiddleware());
builder.UseBranch(
    ctx => ctx.TenantId == "tenant-456",
    branch =>
    {
        branch.Use(new AuditMiddleware());
        branch.Use(new FeatureToggleMiddleware());
    }
);

Declarative, testable, reorderable, inspectable.


Documentation

Getting Started

Guides

Reference


Common Use Cases

Web API Request Pipeline

var builder = new PipelineBuilder<ApiRequestContext>();

// Always execute
builder.Use(new RequestLoggingMiddleware());
builder.Use(new ExceptionHandlingMiddleware());
builder.Use(new AuthenticationMiddleware());
builder.Use(new AuthorizationMiddleware());

// Conditional execution
builder.UseWhen(
    ctx => ctx.Roles.Contains("Admin"),
    new AdminAuditMiddleware()
);

// Process request
builder.Use(new ValidationMiddleware());
builder.Use(new RateLimitingMiddleware());
builder.Use(new RequestProcessingMiddleware());

var executor = new PipelineExecutor<ApiRequestContext>(builder);

See full example →

Multi-Tenant Pipeline

var builder = new PipelineBuilder<TenantContext>();

builder.Use(new LoggingMiddleware());
builder.Use(new TenantResolutionMiddleware());

// Branch by tenant plan
builder.UseBranch(
    ctx => ctx.Plan == "Free",
    freeBranch =>
    {
        freeBranch.Use(new RateLimitingMiddleware(limit: 100));
        freeBranch.Use(new FeatureLimitMiddleware());
    }
);

builder.UseBranch(
    ctx => ctx.Plan == "Enterprise",
    enterpriseBranch =>
    {
        enterpriseBranch.Use(new EnterpriseFeaturesMiddleware());
        enterpriseBranch.Use(new DedicatedSupportMiddleware());
    }
);

builder.Use(new ProcessingMiddleware());

See full example →


Pipeline Diagnostics

Optional debug layer with zero runtime overhead when disabled.

using Core.Features.Pipeline.Diagnostics;

var builder = new PipelineBuilder<RequestContext>();
builder.Use(new LoggingMiddleware());
builder.Use(new ValidationMiddleware());
builder.Use(new ProcessingMiddleware());

var executor = new PipelineExecutor<RequestContext>(builder);

// Enable diagnostics
using var scope = PipelineDebugScope.Begin(out var debug);

await executor.ExecuteAsync(context);

// Inspect execution
foreach (var step in debug.Steps)
{
    Console.WriteLine(
        $"{step.Middleware.GetType().Name} | " +
        $"{step.Duration?.TotalMilliseconds:F3}ms | " +
        $"Next={step.NextCalled}"
    );
}

Output:

LoggingMiddleware | 12.345ms | Next=True
ValidationMiddleware | 5.123ms | Next=True
ProcessingMiddleware | 45.678ms | Next=False

What Gets Captured:

  • Middleware identity
  • Execution duration
  • Whether next() was called
  • Execution order

Learn more about diagnostics →


Runtime Inspection

Inspect and manipulate pipeline structure at runtime.

using Core.Features.Pipeline.Inspection;

var builder = new PipelineBuilder<RequestContext>();
builder.Use(new LoggingMiddleware());
builder.Use(new ValidationMiddleware());
builder.Use(new ProcessingMiddleware());

var inspector = new PipelineInspector<RequestContext>(builder.Middlewares);

// List middleware
foreach (var mw in inspector.GetMiddlewares())
{
    Console.WriteLine(mw.GetType().Name);
}

// Remove middleware dynamically
inspector.Remove(mw => mw is ValidationMiddleware);

// Get metadata
foreach (var descriptor in inspector.GetDescriptors())
{
    Console.WriteLine(
        $"{descriptor.Name} [{descriptor.Kind}] " +
        $"Conditional={descriptor.IsConditional}"
    );
}

Descriptors are resolved via:

  • Attributes
  • Metadata interfaces
  • Descriptor providers
  • Cached resolution (thread-safe)

Learn more about inspection →


Architecture

Application Layer
    │
    ▼
PipelineBuilder<TContext>
    ├─ Use
    ├─ UseWhen
    ├─ UseBranch
    │
    ▼
PipelineExecutor<TContext>
    ├─ Sequential execution
    ├─ Async continuation
    └─ Optional Debug Wrapper
    │
    ▼
Middleware Chain
    ├─ Standard Middleware
    ├─ Conditional Middleware
    └─ Branch Middleware

Execution Model

  • ✅ No reflection in hot path
  • ✅ No dynamic dispatch
  • ✅ Single delegate allocation per middleware
  • ✅ AsyncLocal only when debugging is enabled
  • ✅ Middleware controls continuation (next)

Best Practices

DO ✅

  • Keep middleware small and single-purpose
  • Prefer UseWhen over internal if logic
  • Use diagnostics only in development
  • Treat middleware as stateless

DON'T ❌

  • Perform I/O in diagnostic middleware
  • Use Console.WriteLine inside middleware
  • Mutate pipeline during execution
  • Share middleware instances with state

Read full best practices guide →


API Reference

Core Types

Diagnostics

Inspection

View complete API reference →


Built with ❤️ for .NET developers