{"id":22979706,"url":"https://github.com/verticalsoftware/vertical-pipelines","last_synced_at":"2025-04-05T17:18:00.980Z","repository":{"id":46091610,"uuid":"376141639","full_name":"verticalsoftware/vertical-pipelines","owner":"verticalsoftware","description":null,"archived":false,"fork":false,"pushed_at":"2024-08-10T23:11:57.000Z","size":1231,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"dev","last_synced_at":"2025-02-11T14:23:17.112Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/verticalsoftware.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-06-11T21:02:23.000Z","updated_at":"2024-08-10T22:31:29.000Z","dependencies_parsed_at":"2024-08-10T21:02:16.290Z","dependency_job_id":"9bb92442-4018-4c1a-a261-e38f6768f46d","html_url":"https://github.com/verticalsoftware/vertical-pipelines","commit_stats":{"total_commits":42,"total_committers":1,"mean_commits":42.0,"dds":0.0,"last_synced_commit":"b1a678fb0819160202851352b67a4a6a15aa70f4"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/verticalsoftware%2Fvertical-pipelines","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/verticalsoftware%2Fvertical-pipelines/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/verticalsoftware%2Fvertical-pipelines/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/verticalsoftware%2Fvertical-pipelines/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/verticalsoftware","download_url":"https://codeload.github.com/verticalsoftware/vertical-pipelines/tar.gz/refs/heads/dev","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247369953,"owners_count":20927928,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-15T01:33:30.222Z","updated_at":"2025-04-05T17:18:00.957Z","avatar_url":"https://github.com/verticalsoftware.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# vertical-pipelines\n\nGeneric \"middleware\" pipelines.\n\n![.net](https://img.shields.io/badge/Frameworks-.netstandard2.0-purple)\n![GitHub](https://img.shields.io/github/license/verticalsoftware/vertical-pipelines)\n![Package info](https://img.shields.io/nuget/v/vertical-pipelines.svg)\n\n[![Dev build](https://github.com/verticalsoftware/vertical-pipelines/actions/workflows/dev-build.yml/badge.svg)](https://github.com/verticalsoftware/vertical-pipelines/actions/workflows/dev-build.yml)\n[![Release](https://github.com/verticalsoftware/vertical-pipelines/actions/workflows/release.yml/badge.svg)](https://github.com/verticalsoftware/vertical-pipelines/actions/workflows/release.yml)\n[![codecov](https://codecov.io/gh/verticalsoftware/vertical-pipelines/branch/dev/graph/badge.svg?token=4RNB0XF988)](https://codecov.io/gh/verticalsoftware/vertical-pipelines)\n\n## Motivation\n\nASP.NET Core provides a middleware pipeline to handle HTTP requests. This micro library defines functionality that enable you to construct logic pipelines of your own and control when they are invoked and the contextual data type that is available to the pipeline components.\n\n## Features at a glance\n\n- Use application defined types to pass state around the middleware pipeline\n- Integrate easily with dependency injection providers. \n\n\u003e ⚠️ Heads Up!\n\u003e \n\u003e **Version 3 is a breaking design change**\n\u003e \n\u003e After a lot of reflection, this library is reverting back to a more simple form. Mainly,\n\u003e the design implemented some internal anti-patterns and required a lot of complexity around\n\u003e creation of middleware components and having to differentiate between singleton and per-invocation (scoped)\n\u003e service dependencies. This next iteration leaves the factory patterns of the components entirely\n\u003e up to the client application (and ideally to dependency injection). \n\u003e \n\u003e The reference for version 2 of this library is [here](./README_v2.md), but please note it will no longer be maintained.\n\n## Usage\n\nMiddleware components are implemented using the `IPipelineMiddlware\u003cTContext\u003e` interface. The `TContext` generic paramter is a type of the application's choosing, and represents a contextual data object that is passed along the components of the pipeline. The only constraint is that `TContext` must be a class type.\n\nThe structure of the implementation is very simple.\n\n```csharp\n// Define a contrived context type...\npublic class MyContext\n{\n    public string[] Parameters { get; set; }\n    \n    public IDictionary\u003cstring, object\u003e AdditionalData { get; set; }\n    \n    public object Result { get; set; }\n}\n\n// Construct middleware components\npublic class MiddlewareA : IPipelineMiddleware\u003cMyContext\u003e\n{\n    public MiddlewareA(/* Dependencies */)\n    {\n    }\n    \n    public async Task InvokeAsync(MyContext context,\n        PipelineDelegate\u003cMyContext\u003e next,\n        CancellationToken cancellationToken)\n    {\n        // Perform discrete middleware logic\n        Console.WriteLine(\"Middleware A invoked\");\n        \n        // Pass control to next delegate\n        await next(context, cancellationToken);\n    }\n}\n\npublic class MiddlewareB : IPipelineMiddleware\u003cMyContext\u003e\n{\n    public MiddlewareB(/* Dependencies */)\n    {\n    }\n    \n    public async Task InvokeAsync(MyContext context,\n        PipelineDelegate\u003cMyContext\u003e next,\n        CancellationToken cancellationToken)\n    {\n        // Perform discrete middleware logic\n        Console.WriteLine(\"Middleware A invoked\");\n\n        // Pass control to next delegate\n        await next(context, cancellationToken);\n    }\n}\n\n// Construct the pipeline\nvar pipelineFactory = new PipelineFactory(new IPipelineMiddleware\u003cMyContext\u003e[]\n    {\n        new MiddlewareA(),\n        new MiddlewareB(),\n        new MiddelwareAction(async (context, next, cancelToken) =\u003e \n        {\n            // Perform discrete middleware logic inline\n            Console.Writeline(\"Inline middleware invoked\");\n            \n            // Pass control to next delegate\n            await next(context, cancelToken);\n        });\n    });\n\nvar pipelineDelegate = pipelineFactory.CreatePipeline();\n\nawait pipelineDelegate(context, cancellationToken);\n\n// Output:\n//   Middelware A invoked\n//   Middleware B invoked   \n//   Inline middleware invoked\n```\n\nNotice the different possible behavioral patterns. Middleware can...\n\n1. perform it's discrete logic then pass control to the next middleware.\n2. pass control to the next middleware, then perform it's discrete logic.\n3. perform it's discrete logic and _short circuit_ the rest of the pipeline by not invoking the `next` delegate.\n\n## Dependency Injection\n\nThe simple design allows for simple integration with dependency injection. Consider the following setup that uses [Microsoft Dependency Injection](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-6.0):\n\n```csharp\nvar services = new ServiceCollection();\n\n// TODO: Register singleton services for middleware (e.g. loggers)\n\nservices.AddSingleton\u003cIPipelineMiddleware\u003cMyContext\u003e, MiddlewareA\u003e();\nservices.AddSingleton\u003cIPipelineMiddleware\u003cMyContext\u003e, MiddelwareB\u003e();\nservices.AddSingleton\u003cIPipelineFactory\u003cMyContext\u003e, PipelineFactory\u003cMyContext\u003e\u003e();\nservices.AddSingleton\u003cPipelineDelegate\u003cMyContext\u003e\u003e(provider =\u003e \n{\n    var factory = provider.GetRequiredService\u003cIPipelineFactory\u003cMyContext\u003e\u003e();\n    return factory.CreatePipeline();\n});\n    \nvar serviceProvider = services.BuildServiceProvider();\n\n// For each request where the pipeline is required...\nvar pipelineDelegate = serviceProvider.GetRequiredService\u003cPipelineDelegate\u003cMyContext\u003e\u003e();\n\nawait pipelineDelegate(context, cancellationToken);\n```\n\nIf any middleware requires scoped dependencies, register those components and the pipeline factory as scoped.\n\n\u003e 🔗 Note\n\u003e\n\u003e Use our [dependency injection](https://github.com/verticalsoftware/vertical-pipelines-dependencyinjection) package for easy configuration with Microsoft.Extensions.DependencyInjection.\n\n## Issues or requests\n\nCreate an issue [here](https://github.com/verticalsoftware/vertical-pipelines/issues).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fverticalsoftware%2Fvertical-pipelines","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fverticalsoftware%2Fvertical-pipelines","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fverticalsoftware%2Fvertical-pipelines/lists"}