Dev GuideAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunitySubmit a ticketLog In
GitHubNuGetDev CommunitySubmit a ticket

Pipelines

Overview of pipelines with code samples.

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.

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 Configured 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.

    IPipe interface

    // 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).
    Pipe ordering

    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.

    Override pipe

    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.

    Stop the execution of a pipeline

    public class CustomPipe : IPipe<CreateGetCartLineResultParameter, CreateGetCartLineResultResult>
         {
             public CreateGetCartLineResultResult Execute(IUnitOfWork unitOfWork, CreateGetCartLineResultParameter parameter, CreateGetCartLineResultResult result)
             {
                 // To stop the execution of this pipeline, 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 will execute, but any pipes that follow will not
                 return new CreateGetCartLineResultResult();
             }
         }
    

Pipeline code sample

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

Retrieve Product Pricing Pipeline

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

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

  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.

Pipeline in code

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

Using a Pipeline in Code

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;
     }
 }