Loading...
Area: Episerver B2B Commerce

Working with pipelines

Recommended reading 

Much like handlers, pipelines serve as an extension point because you can add new pipes to any pipeline. This allows you to add new or change existing functionality. For example, the FormatLabel customer pipeline returns a formatted label for describing a customer. By default, this label displays information like the first and last name, address, city, and state of the customer. You could add a pipe that adds more customer information to the label. In order to add a new pipe to an existing pipeline, you must correctly implement and configure the new pipe.

How to Add a New Pipe

In order for a pipe to be used within a pipeline, it must do the following:

  • Implement the IPipe<TIn, TOut> interface
  • Return a result from the "Execute" method
  • Specify an order via the "Order" property

The IPipe<TIn, TOut> interface requires the pipe to implement the "Execute" method, specify pipe order using the "Order" property, and associate the pipe with a pipeline. The "Execute" method is the sole entry point into a pipe. This method also contains all of the business logic used by the pipe to complete it's work. The "Order" property is used to specify the ordering of the pipe within the pipeline.

Pipes are ordered in ascending order within a pipeline using the values specified by each pipe in the "Order" property. Pipes within pipelines start with the pipe with the lowest order value. By default, all pipelines have at least one pipe with an order value of 100. From there, additional pipes usually increment the order by 100. For example, if a pipeline has three pipes, the order values would be 100, 200, and 300. This ordering scheme allows you to both insert a new pipe in between existing pipes or add a new pipe to the start or end of a pipeline. To insert a new pipe into the second position in the pipeline, the pipe could be given an order value between 101 and 199. To add the pipe to the start or end of the pipeline, the pipe should be given an order less than 100 or greater than 300, respectively.

A pipe is associated with a pipeline according to the parameter and result object types (TIn and TOut). All pipes with the same object types will be executed within the same pipeline. Below is a code sample explaining the association further.
// These two pipes are part of the same pipeline // because they both declare the same parameter // and result object types. public class CalculateOrderLines : IPipe<GetCartPricingParameter, GetCartPricingResult> public class CalculateOrderTotal : IPipe<GetCartPricingParameter, GetCartPricingResult> // This pipe is part of a different pipeline because // it declares different parameter and result object types // than the two pipes above. public class GetRoles : IPipe<GetRolesParameter, GetRolesResult>

Once you have configured the new pipe, you can build the solution and run the application. The next time the pipeline executes, your new pipe should be included in the pipeline. To help explain the pipeline further, the following section walks through adding a new pipe to a pipeline.

Add a New Pipeline

The following section adds a new pipe to the FormatLabel customer pipeline. This new pipe will add the customer's zip code to the label.

Preconditions

  • ISC Cloud SDK installed

Steps

  1. In your Extensions project, create a new pipe class named "AddCustomerZipCode".
    public class AddCustomerZipCode { }
  2. Implement the IPipe<TIn, TOut> interface. For the TIn and TOut parameter and result object types, specify FormatLabelParameter and FormatLabelResult, respectively. Remember, this is how a pipe is associated with a pipeline. It's important to specify the correct types.
    public class AddCustomerZipCode : IPipe<FormatLabelParameter, FormatLabelResult> { }
  3. Specify a value for the Order property. Remember, all pipelines start with a pipe that has an order of 100. Within the pipeline, all pipes provided by B2B Cloud increment the order by 100. The FormatLabel pipeline has only one pipe with an order value of 100. Specifying an order of 3000 for the new pipe is overkill, but it ensures that this new pipe comes after all of the original pipes.

    public int Order => 3000;
  4. Implement the Execute method. Remember, this is the sole entry point to the pipe. Additionally, this method must either continue the pipeline or exit it. These methods are explained in the sample below.

    public FormatLabelResult Execute(IUnitOfWork unitOfWork, FormatLabelParameter parameter, FormatLabelResult result)
    {
        if (parameter.Customer == null)
        {
            // If you set the ResultCode to the ResultCode.Error value,
            // the pipeline will exit. You should also specify a more accurate
            // SubCode and error message. The pipe should immediately return
            // to avoid performing unnecessary work.
            result.ResultCode = ResultCode.Error;
            result.SubCode = SubCode.NotFound;
            result.Messages.Add(new ResultMessage { Message = "A customer is required to format the label." });
            return result;
        }
     
        if (!parameter.Customer.PostalCode.IsEmpty())
        {
            result.Label += parameter.Customer.PostalCode;
        }
     
        // If you want to exit the pipeline, but there is no
        // need to indicate an error, you can set the result.ExitPipeline
        // flag to true. The pipe should immediately return to
        // avoid performing unnecessary work.
        result.ExitPipeline = true;
        return result;
     
        // Returning the result without flagging the ExitPipeline property
        // or indicating an error continues execution of the pipeline.
        // Control is handed over to the next pipe.
        return result;
    }

    For reference, below is the completed pipe class.

    public class AddCustomerZipCode : IPipe<FormatLabelParameter, FormatLabelResult>
    {
        public FormatLabelResult Execute(IUnitOfWork unitOfWork, FormatLabelParameter parameter, FormatLabelResult result)
        {
            if (!parameter.Customer.PostalCode.IsEmpty())
            {
                result.Label += parameter.Customer.PostalCode;
            }
     
            return result;
        }
     
        public int Order => 3000;
    }
  5. Build your solution.

If the rest of your application is set up correctly, the next time you select a customer, you should see that the postal code was added to the customer label. The customer label can be viewed in the customer dropdown when selecting a customer or in the header after you log in if you click your username. While adding a new pipe may work for most use cases, you may find a time where you need to replace an existing pipe.

Replace an Existing Pipe

If you find a pipeline that requires additional business logic and inserting a pipe before or after an existing pipe is not sufficient, you may choose to replace the existing pipe. In order to do this, you must configure the new pipe correctly. To replace an existing pipe, the new pipe must:

  • Implement the IPipe<TIn, TOut> interface
  • Return a result from the "Execute" method
  • Specify an order via the "Order" property
  • Be named the same as the pipe being replaced (this is a new requirement)

Using the FormatLabel customer pipeline as an example again, the following pipe will replace the existing FormatLabel pipe.

public class FormatLabel : IPipe<FormatLabelParameter, FormatLabelResult>
{
    // The order value does not need to match the pipe
    // that is being replaced, but it is best practice
    // to match the order value. In this configuration,
    // the pipe has been completely replaced and more
    // pipes can be added to the pipeline.
    public int Order => 100;
 
    public FormatLabelResult Execute(IUnitOfWork unitOfWork, FormatLabelParameter parameter, FormatLabelResult result)
    {
        result.Label = "new format";
 
        return result;
    }
}

When this pipe is implemented, the application will display the text "new format" instead of the customer's name, address, city, and state.

Note: You cannot "remove" a pipe, but you can create an implementation of it that does nothing.

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

Last updated: Dec 11, 2020

Recommended reading