TPL Dataflow provides a very useful feature:
public static IPropagatorBlock<TInput, TOutput> Encapsulate<TInput, TOutput>(
ITargetBlock<TInput> target,
ISourceBlock<TOutput> source)
so that you can encapsulate multiple blocks into one transform block. He returns
IPropagatorBlock<TInput, TOutput>
which represents the start and end blocks of your pipeline.
However, if the last block in my pipeline is an ActionBlock, I cannot use this since the ActionBlock is not a SourceBlock, and the type of the function returned would be ITargetBlock, not IPropagatorBlock.
Essentially, I'm looking for something like this function:
public static ITargetBlock<TStart> Encapsulate<TStart, TEnd>(
ITargetBlock<TStart> startBlock,
ActionBlock<TEnd> endBlock)
Is this a reasonable thing to write, or am I missing something simple? I'm not quite sure how to write it, especially the completion wiring. Should I create my own custom block type?
EDIT:
, , @Panagiotis Kanavos, , . EncapsulatingPropagator, DataflowBlock.Encapsulate:
internal sealed class EncapsulatingTarget<TStart, TEnd> : ITargetBlock<TStart>
{
private readonly ITargetBlock<TStart> startBlock;
private readonly ActionBlock<TEnd> endBlock;
public EncapsulatingTarget(ITargetBlock<TStart> startBlock, ActionBlock<TEnd> endBlock)
{
this.startBlock = startBlock;
this.endBlock = endBlock;
}
public Task Completion
{
get { return this.endBlock.Completion; }
}
public void Complete()
{
this.startBlock.Complete();
}
void IDataflowBlock.Fault(Exception exception)
{
if (exception == null)
{
throw new ArgumentNullException("exception");
}
this.startBlock.Fault(exception);
}
public DataflowMessageStatus OfferMessage(
DataflowMessageHeader messageHeader,
TStart messageValue,
ISourceBlock<TStart> source,
bool consumeToAccept)
{
return this.startBlock.OfferMessage(messageHeader, messageValue, source, consumeToAccept);
}
}