Message Mutators

Component: NServiceBus
NuGet NServiceBus (5.x)

Message Mutators allow mutation of messages in the pipeline.

NServiceBus supports two categories of Message Mutators:

Logical Message Mutators

Message mutators change/react to individual messages being sent or received. The IMutateOutgoingMessages or IMutateIncomingMessages interfaces allow the implementation of hooks for the sending and receiving sides.

Mutators can be used to perform actions such as validation of outgoing/incoming messages.

IMutateIncomingMessages

public class MutateIncomingMessages :
    IMutateIncomingMessages
{
    public object MutateIncoming(object message)
    {
        // return the same instance
        // or optionally create and return a new instance
        return message;
    }
}

IMutateOutgoingMessages

public class MutateOutgoingMessages :
    IMutateOutgoingMessages
{
    public object MutateOutgoing(object message)
    {
        // return the same instance
        // or optionally create and return a new instance
        return message;
    }
}

IMessageMutator

IMessageMutator is an interface that combines both IMutateIncomingMessages and IMutateOutgoingMessages.

Transport Messages Mutators

Transport message mutators work on the serialized transport message and are useful for compression, header manipulation, etc. Create transport message mutators by implementing the IMutateIncomingTransportMessages or IMutateOutgoingTransportMessages interfaces.

IMutateIncomingTransportMessages

public class MutateIncomingTransportMessages :
    IMutateIncomingTransportMessages
{
    public void MutateIncoming(TransportMessage transportMessage)
    {
        // the bytes containing the incoming messages.
        var bytes = transportMessage.Body;

        // optionally replace the Body
        transportMessage.Body = ServiceThatChangesBody.Mutate(transportMessage.Body);

        // the incoming headers
        var headers = transportMessage.Headers;

        // optional manipulate headers

        // add a header
        headers.Add("MyHeaderKey1", "MyHeaderValue");

        // remove a header
        headers.Remove("MyHeaderKey2");
    }
}

IMutateOutgoingTransportMessages

public class MutateOutgoingTransportMessages :
    IMutateOutgoingTransportMessages
{
    public void MutateOutgoing(LogicalMessage logicalMessage, TransportMessage transportMessage)
    {
        // the outgoing message instance
        var instance = logicalMessage.Instance;

        // the bytes containing the serialized outgoing messages.
        var bytes = transportMessage.Body;

        // optionally replace the Body.
        // this can be done using either the information from the logicalMessage or transportMessage
        transportMessage.Body = ServiceThatChangesBody.Mutate(logicalMessage.Instance);

        // the outgoing headers
        var headers = transportMessage.Headers;

        // optional manipulate headers

        // add a header
        headers.Add("MyHeaderKey1", "MyHeaderValue");

        // remove a header
        headers.Remove("MyHeaderKey2");
    }
}

IMutateTransportMessages

IMutateTransportMessages is an interface that combines both IMutateIncomingTransportMessages and IMutateOutgoingTransportMessages.

Registering a Mutator

Mutators are NOT automatically registered in the container, so to have them invoked, register them in the EndpointConfiguration:

busConfiguration.RegisterComponents(
    registration: components =>
    {
        components.ConfigureComponent<MyMutator>(DependencyLifecycle.InstancePerCall);
    });
Mutators are non-deterministic in terms of order of execution. If more fine grained control is required over the pipeline see Pipeline Introduction.

When a mutator throws an exception

If a incoming mutator throws an exception, the message aborts, rolls back to the queue, and recoverability is applied.

If an outgoing mutator throws an exception, the exception bubbles up to the method performing the Send or Publish. If the operation is performed on a context in the pipeline the message aborts, rolls back to the queue, and recoverability is applied. If the operation is performed on the message session the exception might bubble up to the user code or tear down the application domain if not properly handled.

Mutators versus Behavior

Shared concepts and functionality

  • Can manipulate pipeline state
  • Can be executed in the incoming or outgoing pipeline
  • Exceptions cause bubble up the pipeline and are handled by the Recoverability

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
Effects call stack depthNoYes
Can replace existing BehaviorNoYes

Samples

  • Message Mutators
    Change messages by plugging custom logic in to a couple of interfaces, encrypting as required.

Related Articles


Last modified