@chrlschn - C# Discriminated Unions and .NET Channels - Jul 6, 2024 - Summary.NET channels are an easy way to achieve high throughput, concurrent/parallel processing of information without the need to manage common challenges with thread synchronization. Discriminated unions pair nicely with .NET channels as a way to process and output different types of data simultaneously into one stream. When used together, .NET channels with discriminated unions make it clean and easy to process and combine streams of data. If you’ve used TypeScript or F# or any language that natively supports DUs, then you’re already familiar with it. C# currently does not natively support discriminated unions as a language feature, but it has been on the roadmap now for several months (years?). Today, it is possible to easily add DU-like patterns to your C# codebase using two .NET libraries: OneOf and dunet. Both make it easy to progressively adopt DUs in C# codebases. There are a lot of benefits and use cases for DUs, but one of my favorites is using them in conjunction with .NET channels because it is common that there is a need to process a single record with different logic and then combine the results at the end. Typically, the code to do so might look like this: foreach (var record in records) { // Serially execute each action. var result1 = await ProcessingAction1Async(record); var result2 = await ProcessingAction2Async(record); var result3 = await ProcessingAction3Async(record); // Get all of the results and perform the final step. await FinalActionAsync(result1, result2, result3); } This is fine if each step is fast and cannot be parallelized. But what if each step is costly and actually discrete (no dependency on the previous result)? Processing all three steps in parallel would make better use of hardware and improve the throughput of the system. Today, we’ll take a look at how to use discriminated unions in C# with .NET channels to achieve exactly that. The Use Case Imagine that the design is for a system that is processing a CSV file that has a list of call logs and the processing pipeline needs to run a series of prompts over the call record in parallel to: Determine the urgency of the call Decide which department the call should be routed to (independently of the urgency) Extract the keywords associated with the call so we can tag it and find it later Since these actions are all discrete, each can be processed in parallel (e.g. using different LLM prompts) and then each facet of the call log can simply be aggregated at the end into a single record to be routed. A perfect use case for .NET channels and discriminated unions. The Discriminated Union There are at least three reasonable ways to model the data structures for the flow in this case: Use a base class; but the various parts are mostly discrete aside from sharing the ID of the call record. Use a generic class like Fragment<T> where T is the type of the fragment, but this has some ergonomic issues when it comes to handling the actual action on the aggregation side. Use a discriminated union! Since three types of facets can be produced fro