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 of 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 registered using:
busConfiguration.RegisterComponents(
registration: components =>
{
components.ConfigureComponent<MyMutator>(DependencyLifecycle.InstancePerCall);
});
When a mutator throws an exception
If an 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 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.
Mutator | Behavior | |
---|---|---|
Complexity to implement | Low | High |
Flexibility | Low | High |
Location in pipeline | Fixed | Flexible |
Complexity to test | Low | Medium* |
Can control nested action | No | Yes |
Affects call stack depth | No | Yes |
Can replace an existing behavior | No | Yes |
- For more information refer to the testing behaviors unit testing sample.