Loading...
Area: Episerver B2B Commerce

Complete guide to pipelines

Recommended reading 

Summary

Pipelines are constructed from individual objects, called Pipes, that contain reusable business logic.

Each pipe is responsible for completing a single, specific task.

All of the pipes in a pipeline work together to complete a bigger, more complex task.

Pipes

All pipes must implement the IPipe<> interface, specifying the parameter and result object type according to the related pipeline.

Code Sample: IPipe<> Example

public interface IPipe<TIn, TOut> : IMultiInstanceDependency, IExtension
{
    int Order { get; }
 
    TOut Execute(IUnitOfWork unitOfWork, TIn parameter, TOut result);
}
  • Typically, a pipe will operate on data within the result object or add data to the result object.
  • Each pipe MUST return the result object when it has completed it's work. The result object will determine whether or not execution of the pipeline is continued.
  • Pipes are ordered within a pipeline and are executed in that order.
  • The order of a pipe within a pipeline is configured in code using a class property.
  • When adding a pipe to an existing pipeline, be aware of the order of existing pipes.
  • The first pipe in any pipeline will have an order of 100. From there, the order of subsequent pipes will be higher than that number. The pipes provided by Epi B2B Commerce will typically increment the order by 100 for each subsequent pipe. For example, the CreateGetCartLineResult pipeline has four pipes, whose order is displayed below.
  • Pipes in the same pipeline will implement the IPipe interface, specifying the same object type for the parameter and result. The example below explains it further.

Code Sample: IPipe Interface Example

// The GetInventory and CreateGetCartLineResult pipes will execute in the same pipeline.
public sealed class GetInventory : IPipe<CreateGetCartLineResultParameter, CreateGetCartLineResultResult>
public sealed class CreateGetCartLineResult : IPipe<CreateGetCartLineResultParameter, CreateGetCartLineResultResult>
 
// The CalculatePrice pipe will execute in a separate pipeline from the two pipes above.
public sealed class CalculatePrice : IPipe<GetProductPricingParameter, GetProductPricingResult>

  • Pipes are ordered and executed in ascending order.
  • You cannot inherit from an existing pipe. If you need to modify the data created by a pipe, instead insert a new pipe after that pipe.
  • To insert a new pipe into a pipeline, create a class that implements IPipe with the appropriate parameter and result types. Then, indicate an order that is appropriate and different from any of the existing pipes. The custom pipe below will be executed after the standard CreateGetCartLineResult pipe, which is in order 100 (as seen above).

Code Sample: Pipe Ordering Example

public class CustomPipe : IPipe<CreateGetCartLineResultParameter, CreateGetCartLineResultResult>
{
    // Depending on the specific pipeline, this will place the pipe in a specific position.
    public int Order => 110;
 
    public CreateGetCartLineResultResult Execute(IUnitOfWork unitOfWork, CreateGetCartLineResultParameter parameter, CreateGetCartLineResultResult result)
    {
        // ...
    }
}
  • To override an existing pipe in a pipeline, you simply name the custom pipe with the same pipe that should be replaced. The custom pipe below will be executed instead of the standard CreateGetCartLineResult pipe. The order does not necessarily need to be the same as the standard pipe.

Code Sample: Override Existing Pipe Example

public class CreateGetCartLineResult : IPipe<CreateGetCartLineResultParameter, CreateGetCartLineResultResult>
{
    // This pipe replaces the CreateGetCartLineResult pipe because the same order is configured.
    public int Order => 100;
 
    public CreateGetCartLineResultResult Execute(IUnitOfWork unitOfWork, CreateGetCartLineResultParameter parameter, CreateGetCartLineResultResult result)
    {
        // ...
    }
}
  • At any point during the lifetime of the pipeline, an individual pipe may stop the execution of a pipeline. To do this, the pipe must either assign an error code to the result object or set the exit flag on the result object.

Code Sample: Stop the Execution of a Pipeline Example

public class CustomPipe : IPipe<CreateGetCartLineResultParameter, CreateGetCartLineResultResult>
{
    public CreateGetCartLineResultResult Execute(IUnitOfWork unitOfWork, CreateGetCartLineResultParameter parameter, CreateGetCartLineResultResult result)
    {
        // Oops... something happened, let's stop execution of this pipeline.
        // To do so, you can either return an error code in the result.
        result.ResultCode = ResultCode.Error;
 
        // Or you can set this flag.
        result.ExitPipeline = true;
 
        // The rest of the method with still execute, but any pipes
        // that follow will not be executed.
        return new CreateGetCartLineResultResult();
    }
}

Code Sample

  • Below is an excerpt from a pipeline that retrieves product pricing. This pipeline contains only one pipe.

Code Sample: Retrieve Product Pricing Pipeline Excerpt

public sealed class CalculatePrice : IPipe<GetProductPricingParameter, GetProductPricingResult>
{
    private readonly IPricingServiceFactory pricingServiceFactory;
 
    private readonly Lazy<ITranslationLocalizer> translationLocalizer;
 
    public CalculatePrice(IPricingServiceFactory pricingServiceFactory, Lazy<ITranslationLocalizer> translationLocalizer)
    {
        this.pricingServiceFactory = pricingServiceFactory;
        this.translationLocalizer = translationLocalizer;
    }
 
    public int Order => 100;
 
    public GetProductPricingResult Execute(IUnitOfWork unitOfWork, GetProductPricingParameter parameter, GetProductPricingResult result)
    {
        foreach (var pricingServiceParameter in parameter.PricingServiceParameters)
        {
            var parameterWithOrderLine = pricingServiceParameter.Value as PricingServiceParameterWithOrderLine;
            var pricingService = this.pricingServiceFactory.GetPricingService(parameterWithOrderLine?.CustomerOrderId);
 
            var pricingServiceResult = pricingService.CalculatePrice(pricingServiceParameter.Value);
            var saveText = SiteContext.Current.LanguageDto != null
                ? this.translationLocalizer.Value.TranslateLabel("Save")
                : string.Empty;
            var productPriceDto = new ProductPriceDto(pricingServiceResult, saveText);
            result.ProductPriceDtos.Add(pricingServiceParameter.Key, productPriceDto);
        }
 
        return result;
    }
}

Pipes Order

  • Epi B2B Commerce provides a Visual Studio extension that will visually show the order of the pipes within a pipeline.

View Pipes Order in Visual Studio

  1. Right-click on the custom pipe.
  2. Select the Show Pipes Order menu item. A window will pop open that shows each the custom pipes in the order they execute.

Use in Code

  • To use a pipeline in code, simply have an instance injected into the class constructor like most other objects.

Code Sample: Using a Pipeline in Code Example

public class CustomHandler : HandlerBase<AddCartLineCollectionParameter, AddCartLineCollectionResult>
{
    private readonly IPricingPipeline pricingPipeline;
 
    public RecalculateCart(IPricingPipeline pricingPipeline)
    {
        this.pricingPipeline = pricingPipeline;
    }
 
    public override AddCartLineCollectionResult Execute(IUnitOfWork unitOfWork, AddCartLineCollectionParameter parameter, AddCartLineCollectionResult result)
    {
        var getCartPricingResult = this.pricingPipeline.GetCartPricing(new GetCartPricingParameter(result.GetCartResult.Cart));
 
        // Do any processing here.
 
        return result;
    }
}

 

Do you find this information helpful? Please log in to provide feedback.

Last updated: Dec 11, 2020

Recommended reading