{"id":36924105,"url":"https://github.com/Open-NET-Libraries/Open.ChannelExtensions","last_synced_at":"2026-01-19T18:00:40.803Z","repository":{"id":41345778,"uuid":"153562602","full_name":"Open-NET-Libraries/Open.ChannelExtensions","owner":"Open-NET-Libraries","description":"A set of extensions for optimizing/simplifying System.Threading.Channels usage.","archived":false,"fork":false,"pushed_at":"2025-11-18T14:06:43.000Z","size":944,"stargazers_count":499,"open_issues_count":0,"forks_count":28,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-11-18T14:47:35.940Z","etag":null,"topics":["async","channel","channels","dotnet","extensions","tasks","threading"],"latest_commit_sha":null,"homepage":"https://open-net-libraries.github.io/Open.ChannelExtensions/api/Open.ChannelExtensions.Extensions.html#methods","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/Open-NET-Libraries.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,"zenodo":null}},"created_at":"2018-10-18T04:15:12.000Z","updated_at":"2025-11-18T14:06:48.000Z","dependencies_parsed_at":"2024-01-11T19:50:35.041Z","dependency_job_id":"32119dfb-adad-4d33-82dc-cc4ce88c628e","html_url":"https://github.com/Open-NET-Libraries/Open.ChannelExtensions","commit_stats":{"total_commits":130,"total_committers":4,"mean_commits":32.5,"dds":0.4,"last_synced_commit":"d6aa801b69e4bdd5dfe6fe4035cdc9fa2687047d"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/Open-NET-Libraries/Open.ChannelExtensions","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Open-NET-Libraries%2FOpen.ChannelExtensions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Open-NET-Libraries%2FOpen.ChannelExtensions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Open-NET-Libraries%2FOpen.ChannelExtensions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Open-NET-Libraries%2FOpen.ChannelExtensions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Open-NET-Libraries","download_url":"https://codeload.github.com/Open-NET-Libraries/Open.ChannelExtensions/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Open-NET-Libraries%2FOpen.ChannelExtensions/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28578952,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-19T17:42:58.221Z","status":"ssl_error","status_checked_at":"2026-01-19T17:40:54.158Z","response_time":67,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["async","channel","channels","dotnet","extensions","tasks","threading"],"created_at":"2026-01-12T19:00:25.486Z","updated_at":"2026-01-19T18:00:40.792Z","avatar_url":"https://github.com/Open-NET-Libraries.png","language":"C#","funding_links":[],"categories":["C# #"],"sub_categories":[],"readme":"# Open.ChannelExtensions\n\n[![NuGet](https://img.shields.io/nuget/v/Open.ChannelExtensions.svg?style=flat)](https://www.nuget.org/packages/Open.ChannelExtensions/)\n\nA set of extensions for optimizing/simplifying System.Threading.Channels usage.\n\n[Click here for detailed documentation.](https://open-net-libraries.github.io/Open.ChannelExtensions/api/Open.ChannelExtensions.Extensions.html#methods)\n\n## Highlights\n\n### Read \u0026 Write\n\n*With optional concurrency levels.*\n\n* Reading all entries in a channel.\n* Writing all entries from a source to a channel.\n* Piping (consuming) all entries to a buffer (channel).\n* `.AsAsyncEnumerable()` (`IAsyncEnumerable`) support for .NET Standard 2.1+ and .NET Core 3+\n\n### Special `ChannelReader` Operations\n\n* `Filter`: reads from the channel until a match is found.\n* `Transform`: applies a transform function upon successfully reading an item from the channel.\n* `Batch`: attempts to group items into a `List\u003cT\u003e` (or a `Queue\u003cT\u003e`) before being available for reading.\n* `Join`: combines batches into a single channel.\n\n---\n## Installation\n\n```nuget\nInstall-Package Open.ChannelExtensions\n```\n---\n\n## Examples\n\nBeing able to define an asynchronous pipeline with best practice usage using simple expressive syntax:\n\n```cs\nawait Channel\n    .CreateBounded\u003cT\u003e(10)\n    .SourceAsync(source /* IEnumerable\u003cTask\u003cT\u003e\u003e */)\n    .PipeAsync(\n        maxConcurrency: 2,\n        capacity: 5,\n        transform: asyncTransform01)\n    .Pipe(transform02, /* capacity */ 3)\n    .ReadAllAsync(finalTransformedValue =\u003e {\n        // Do something async with each final value.\n    });\n```\n\n```cs\nawait source /* IEnumerable\u003cT\u003e */\n    .ToChannel(boundedSize: 10, singleReader: true)\n    .PipeAsync(asyncTransform01, /* capacity */ 5)\n    .Pipe(\n        maxConcurrency: 2,\n        capacity: 3,\n        transform: transform02)\n    .ReadAll(finalTransformedValue =\u003e {\n        // Do something with each final value.\n    });\n```\n\n### Reading (until the channel is closed)\n\n#### One by one read each entry from the channel\n\n```cs\nawait channel.ReadAll(\n    entry =\u003e { /* Processing Code */ });\n```\n\n```cs\nawait channel.ReadAll(\n    (entry, index) =\u003e { /* Processing Code */ });\n```\n\n```cs\nawait channel.ReadAllAsync(\n    async entry =\u003e { await /* Processing Code */ });\n```\n\n```cs\nawait channel.ReadAllAsync(\n    async (entry, index) =\u003e { await /* Processing Code */ });\n```\n\n#### Read concurrently each entry from the channel\n\n```cs\nawait channel.ReadAllConcurrently(\n    maxConcurrency,\n    entry =\u003e { /* Processing Code */ });\n```\n\n```cs\nawait channel.ReadAllConcurrentlyAsync(\n    maxConcurrency,\n    async entry =\u003e { await /* Processing Code */ });\n```\n\n### Writing\n\nIf `complete` is `true`, the channel will be closed when the source is empty.\n\n#### Dump a source enumeration into the channel\n\n```cs\n// source can be any IEnumerable\u003cT\u003e.\nawait channel.WriteAll(source, complete: true);\n```\n\n```cs\n// source can be any IEnumerable\u003cTask\u003cT\u003e\u003e or IEnumerable\u003cValueTask\u003cT\u003e\u003e.\nawait channel.WriteAllAsync(source, complete: true);\n```\n\n#### Synchronize reading from the source and process the results concurrently\n\n```cs\n// source can be any IEnumerable\u003cTask\u003cT\u003e\u003e or IEnumerable\u003cValueTask\u003cT\u003e\u003e.\nawait channel.WriteAllConcurrentlyAsync(\n    maxConcurrency, source, complete: true);\n```\n\n### Filter \u0026 Transform\n\nBoth of these extensions operate synchronously after an item is read from the channel.\n\u003e Any predicate or selector function must trap errors of the downstream read will fail and data may not be recoverable.\n\n```cs\n// Filter and transform when reading.\nchannel.Reader\n    .Filter(predicate) // .Where()\n    .Transform(selector) // .Select()\n    .ReadAllAsync(async value =\u003e {/*...*/});\n```\n\n### Batching\n\n```cs\nvalues.Reader\n    .Batch(10 /*batch size*/) // Groups into List\u003cT\u003e.\n    .WithTimeout(1000) // Any non-empty batches are flushed every second.\n    .ReadAllAsync(async batch =\u003e {/*...*/});\n```\n\n### Joining\n\nThe inverse of batching.\n\n```cs\nbatches.Reader\n    .Join() // Combines the batches into a single channel.\n    .ReadAllAsync(async value =\u003e {/*...*/});\n```\n\n### Pipelining / Transforming\n\n#### Transform and buffer entries\n\n```cs\n// Transform values in a source channel to new unbounded channel.\nvar transformed = channel.Pipe(\n    async value =\u003e /* transformation */);\n```\n\n```cs\n// Transform values in a source channel to new unbounded channel with a max concurrency of X.\nconst int X = 4;\nvar transformed = channel.Pipe(\n    X, async value =\u003e /* transformation */);\n```\n\n```cs\n// Transform values in a source channel to new bounded channel bound of N entries.\nconst int N = 5;\nvar transformed = channel.Pipe(\n    async value =\u003e /* transformation */, N);\n```\n\n```cs\n// Transform values in a source channel to new bounded channel bound of N entries with a max concurrency of X.\nconst int X = 4;\nconst int N = 5;\nvar transformed = channel.Pipe(\n    X, async value =\u003e /* transformation */, N);\n\n// or\ntransformed = channel.Pipe(\n    maxConcurrency: X,\n    capacity: N,\n    transform: async value =\u003e /* transformation */);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FOpen-NET-Libraries%2FOpen.ChannelExtensions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FOpen-NET-Libraries%2FOpen.ChannelExtensions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FOpen-NET-Libraries%2FOpen.ChannelExtensions/lists"}