Search code examples
c#extension-methodsparallel-extensions

Creating extensions for Pipeline from Parallel Extensions Extras


I truly enjoy working with Parallel Extensions Extras, but must admit, still find myself challenged when it comes to creating extensions for Fluent programming methods.

I have a particular need, given this simple BusinessEntity following the Null Object Pattern

public abstract class BusinessEntity
{
    public static readonly BusinessEntity Null = new NullBusinessEntity();
    public long EntityKey { get; set; }

    private class NullBusinessEntity : BusinessEntity
    {
    }
}

How would I go about creating a "conditional Next" step, which would execute the body of the function only if a condition was met?

For now this is what I do:

Func<BusinessEntity, Func<BusinessEntity,BusinessEntity>, BusinessEntity> conditional = (be, func) =>
{
    if (be.Equals(BusinessEntity.Null)) return be;
    return func.Invoke(be);
};

Then in the pipeline I invoke it like so:

Pipeline<BusinessEntity, string> pipeline = Pipeline.Create<BusinessEntity, BusinessEntity>(CheckEntity)
    .Next<BusinessEntity>(be => conditional.Invoke(be, entity => ProcessEntity(be)))
    .Next<string>(FinalResult);

Is there a way to create an Extension to the Pipeline, specific to my BusinessEntity (or not), which would allow me to call it like this:

Pipeline<BusinessEntity, string> pipeline = Pipeline
    .Create<BusinessEntity, BusinessEntity>(CheckEntity)
    .ConditionalNext<BusinessEntity>(ProcessEntity)
    .Next<string>(FinalResult);

Is that possible?


Solution

  • If you wanted to make a generic extension for this that's conditional on null, you just need to write an extension method that invokes Next() with your conditional lambda like:

    public static Pipeline<TInput, TNextOutput>
        ConditionalNext<TInput, TOutput, TNextOutput>(
        this Pipeline<TInput, TOutput> pipeline, Func<TOutput, TNextOutput> func)
        where TOutput : class
        where TNextOutput : class
    {
        return pipeline.Next(x => x == null ? null : func(x));
    }
    

    The problem with the null object pattern is that it's not usable in generic methods, so you will have to make it specific to your type:

    public static Pipeline<TInput, BusinessEntity> ConditionalNext<TInput>(
        this Pipeline<TInput, BusinessEntity> pipeline,
        Func<BusinessEntity, BusinessEntity> func)
    {
        return pipeline.Next(x => x.Equals(BusinessEntity.Null) ? x : func(x));
    }
    

    In both cases, the usage would look like this:

    var pipeline = Pipeline.Create<BusinessEntity, BusinessEntity>(CheckEntity)
        .ConditionalNext(ProcessEntity)
        .Next(FinalResult);