While building a data processing pipeline with TPL Dataflow, I needed to route messages to different blocks based on specific conditions. The LinkTo method's predicate parameter is the feature I needed to create branching logic in my dataflow network.
In this post, I explore how to use predicates to build conditional flows that are both efficient and maintainable.
Understanding LinkTo predicates
The LinkTo method in TPL Dataflow connects a source block to a target block, creating a pipeline for data to flow through. The method signature includes an optional predicate parameter:
The predicate is a function that evaluates each message and returns true if the message should be sent to the target block, or false if it should be offered to the next linked block in the chain.
A simple example
Let's start with a straightforward example that demonstrates the basic concept. We'll create a pipeline that routes even numbers to one block and odd numbers to another:
How predicate evaluation works
When a message is ready to leave a source block, TPL Dataflow evaluates the predicates in the order the links were created. The first link whose predicate returns true receives the message. If no predicate matches, the message is dropped unless you've set up a catch-all link.
IMPORTANT: This ordering is critical and gives you fine-grained control over message routing.
Creating a ‘catch-all’ path
To ensure no messages are lost, you should always include a default path for messages that don't match any specific condition. You can do this by adding a final link without a predicate:
Real-world example: Processing orders by priority
Here's a more realistic scenario where we process orders based on their priority and value:
Handling unlinked messages
By default, if a message doesn't match any predicate, it's declined and remains in the source block. To handle this gracefully, you have a few options:
So you should always add an alternative path (such as DataflowBlock.NullTarget) when using a predicate with LinkTo in TPL Dataflow.
Why? When you use a predicate in LinkTo, only items matching the predicate are sent to the target block. Items that do not match are left unprocessed unless you provide another link for them. If you do not handle these items, the source block will never complete, because it waits for all items to be consumed.