Manipulate Pipeline with Behaviors

Component: NServiceBus
NuGet Package NServiceBus (4.x)
Standard support for version 4.x of NServiceBus has expired. For more information see our Support Policy.

Pipelines are made up of a group of steps acting on the same level of abstraction. This allows scenarios such as

  • Defining a step that works with the "incoming physical" message before it has been deserialized.
  • Defining a step that is executed before and after each handler invocation (remember: there can be multiple message handlers per message).

Extending the pipeline is done with a custom behavior implementing Behavior<TContext>.TContext is the context of the stage that the behavior belongs to.

4.5 NServiceBus
public class SampleBehavior :
    IBehavior<HandlerInvocationContext>
{
    public void Invoke(HandlerInvocationContext context, Action next)
    {
        // custom logic before calling the next step in the pipeline.

        next();

        // custom logic after all inner steps in the pipeline completed.
    }
}

In the above code snippet the SampleBehavior class derives from the Behavior contract and targets the incoming context. This tells the framework to execute this behavior after the incoming raw message has been deserialized and a matching message type has been found. At runtime, the pipeline will call the Invoke method of each registered behavior passing in as arguments the current message context and an action to invoke the next behavior in the pipeline.

Each behavior is responsible to call the next step in the pipeline chain by invoking next().

Add a new step

To add a custom behavior to the pipeline define a step for it:

4.5 NServiceBus
class NewStepInPipeline :
    PipelineOverride
{
    public override void Override(BehaviorList<HandlerInvocationContext> behaviorList)
    {
        behaviorList.InsertAfter<InvokeHandlersBehavior, SampleBehavior>();
    }

    // Classes inheriting from PipelineOverride are registered by convention.
    // No need to explicitly register.
}

Replace an existing step

To replace the implementation of an existing step replace it with a custom behavior:

4.5 NServiceBus
class ReplaceExistingBehavior :
    PipelineOverride
{
    public override void Override(BehaviorList<HandlerInvocationContext> behaviorList)
    {
        behaviorList.Replace<InvokeHandlersBehavior, MyInvokeHandlersBehavior>();
    }

    // Classes inheriting from PipelineOverride are registered by convention.
    // No need to explicitly register.
}

In order to replace the existing step it is necessary to provide a step id. The most reliable way of determining the step id, is to find the step definition in the NServiceBus source code.

Note, however, that step ids are hard-coded strings and may change in the future resulting in an unexpected behavior change. In case of replacing built-in steps, create automatic tests that will detect potential id change or step removal.

Steps can also be registered from a Feature.

Exception Handling

Exceptions thrown from a behavior's Invoke method bubble up the chain. If the exception is not handled by a behavior, the message is considered as faulted which results in putting the message back in the queue (and rolling back the transaction) or moving it to the error queue (depending on the endpoint configuration).

Mutators versus Behaviors

Shared concepts and functionality

Both mutators and behaviors:

  • Can manipulate pipeline state
  • Can be executed in the incoming or outgoing pipeline
  • Bubble exceptions up the pipeline and handle them by the recoverability mechanism

Differences

Note that these are relative differences. So, for example, a behavior is only "high complexity" in comparison to a mutator.

MutatorBehavior
Complexity to implementLowHigh
FlexibilityLowHigh
Location in pipelineFixedFlexible
Complexity to testLowMedium*
Can control nested actionNoYes
Affects call stack depthNoYes
Can replace an existing behaviorNoYes

Related Articles


Last modified