I have previously stated my enthusiasm for the Sitecore Pipeline concept. This article describes how to create your own, how to add parameters, and which properties to use.
WHAT IS A SITECORE PIPELINE?
It is best described as a simple form of dependency injection pattern, or maybe more of a config-file-configurable method chaining pattern. The idea is, that if you have a task to perform, and this task requires several independent sub-tasks to run in sequence, you can split these sub-tasks into separate methods (called processors), and then run them in sequence using the same parameters for each method.
The best known Sitecore pipeline is the httpRequestBegin pipeline that runs for every request:
<httpRequestBegin>
<processor type="Sitecore.Pipelines.PreprocessRequest.CheckIgnoreFlag, Sitecore.Kernel" />
..
<processor type="Sitecore.Pipelines.HttpRequest.SiteResolver, Sitecore.Kernel" />
<processor type="Sitecore.Pipelines.HttpRequest.UserResolver, Sitecore.Kernel" />
...
<processor type="Sitecore.Pipelines.HttpRequest.ExecuteRequest, Sitecore.Kernel" />
</httpRequestBegin>
WHY NOT JUST MAKE ONE BIG METHOD? OR ONE CLASS THAT USES DEPENDENCY INJECTION?
Pipelines give you the following advantages:
- Extensibility: You can easily add another processor to any pipeline, thus adding to the functionality of that task.
- Replaceability: You can easily replace any processor with one of your own, replacing the way Sitecore works.
- Testability: Pipelines allow you to break your code into very small processors. Any change of one processor does not interfere with any of the other processors, making testing easier.
- It’s easy: It’s really easy to work with. It’s standard .NET code.
ENOUGH SMALL TALK, LETS MAKE A PIPELINE:
A pipeline consists of a class or Arguments, inheriting from Sitecore.Pipelines.PipelineArgs, and a configuration section that describes which tasks (processors) to execute in which sequence.
The arguments class is usually a POCO class with getters and setters:
public class MyArgs : PipelineArgs
{
public string Argument1
{
get; set;
}
public int Argument2
{
get; set;
}
}
The configuration section determines the pipeline name, and which processors to run:
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:env="http://www.sitecore.net/xmlconfig/env/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
<sitecore>
<pipelines>
<my_pipeline>
<processor type="MyCode.MyPipeline.MyProcessor1, MyDll"/>
<processor type="MyCode.MyPipeline.MyProcessor2, MyDll"/>
<processor type="MyCode.MyPipeline.MyProcessor3, MyDll"/>
</my_pipeline>
</pipelines>
</sitecore>
</configuration>
HOW DOES THE PROCESSOR LOOK LIKE?
The processor needs a method called “Process” that takes one argument, “args” of the “MyArgs” type:
namespace MyCode.MyPipeline
{
public class MyProcessor1
{
public void Process(MyArgs args)
{
try
{
DoSomething(args.Argument1);
args.Argument2 = DoAnotherThing();
}
catch (Exception ex)
{
args.AddMessage("An error occurred");
args.AbortPipeline();
}
}
}
}
HOW TO EXECUTE A PIPELINE:
The CorePipeline.Run() method executes a pipeline:
MyArgs args = new MyArgs();
args.Argument1 = "my string";
args.Argument2 = 42;
CorePipeline.Run("my_pipeline", args);
if (args.Aborted)
{
foreach (var message in args.GetMessages())
Log.Error(message.Text, this);
}
HOW TO ADD PARAMETERS TO A PIPELINE:
If you add a subitem to the configuration:
<processor type="MyCode.MyPipeline.MyProcessor1, MyDll">
<MyParameter>value</MyParameter>
</processor
This subitem will by magic appear in any property of the same name in the processor:
namespace MyCode.MyPipeline
{
public class MyProcessor1
{
public string MyParameter { get; set; }
public void Process(MyArgs args)
{
// value of MyParameter is "value"
}
}
}
In fact the same advanced configuration rules apply to pipelines as all other Sitecore items.
PROCESSOR ATTRIBUTES:
You can decorate your processors with attributes:
<processor type="MyCode.MyPipeline.MyProcessor1, MyDll" method="MyMethod" name="MyName" runIfAborted="false" resolve="false" reusable="false"/>
- type: The class to run
- method: The method name to run. If not included, you use the name “Process”
- name: Just a descriptive name
- runIfAborted: Processors with this attribute set to true will run even if the pipeline is aborted. This is useful as a cleanup property if something went wrong.
- resolve: Set this property to true to allow the Sitecore Dependency Injection engine to resolve the type name to run.
- reusable: This property allows your processor to be run in a transient scope.
GOOD PRACTISES:
- If you throw an exception in any processor, the pipeline will stop its execution, and the CorePipeline.Run() also throws an exception. This means that your “runIfAborted=true” processor will not run. You should therefore catch any exception in the processor, and use the args.AbortPipeline() method to control execution flow.
- Make your processors as atomic as possible. Some of my best processors consists of one line of code. Your processor should do one task and one task only.
- You can debug the performance of your processors, but this slows down the performance of all pipelines, so use this with caution.
- Processors are only created once and kept alive for the entire application lifetime. Any constructors are only called once and local variables are not reset, so treat your processors as a stateless piece of code.
This is true unless you use the reusable=”false” property, but beware that object construction takes time and this feature will slow down your pipeline.
- Any configuration error will result in an exception when CorePipeline.Run() is called. If the method or class is not found for one processor, the whole pipeline is not executed.
MORE TO READ: