Getting Started
Architecture
NServiceBus
Transports
Persistence
ServiceInsight
ServicePulse
ServiceControl
Monitoring
Samples

Change/Move Message Type

Component: NServiceBus
NuGet Package: NServiceBus (9.x)

This sample illustrates an approach for changing a message type. This includes any one, or multiple, of the following scenarios:

  • Moving a message type between assemblies.
  • Renaming a message type.
  • Renaming the assembly containing the message type.
  • Adding, removing or changing the strong name of the assembly containing the message type
This sample uses 2 "Phase" Endpoint Projects to illustrate the iterations of a single endpoint in one solution.

Scenario

This sample shows moving a message using multiple of the above mentioned scenarios.

Phase 1

In the Phase 1 endpoint the message type is:

  • Named CreateOrderPhase1.
  • Exists in an assembly named SamplePhase1.
  • Exists in an assembly that is not strong named.

Phase 2

In the Phase 2 endpoint the message type is:

  • Named CreateOrderPhase2.
  • Exists in an assembly named SamplePhase2.
  • Exists in an assembly that is strong named.

Mutation

This change is achieved via the use of a IMutateIncomingTransportMessages.

The mutator is registered at endpoint startup:

endpointConfiguration.RegisterMessageMutator(new MessageIdentityMutator());

The mutator then replaces the NServiceBus.EnclosedMessageTypes header via the use of Type.GetType(typeName, assemblyResolver, typeResolver) API.

class MessageIdentityMutator :
    IMutateIncomingTransportMessages
{
    public Task MutateIncoming(MutateIncomingTransportMessageContext context)
    {
        var headers = context.Headers;
        var messageTypeKey = "NServiceBus.EnclosedMessageTypes";

        if (!headers.TryGetValue(messageTypeKey, out var messageType))
        {
            return Task.CompletedTask;
        }

        var type = Type.GetType(
            typeName: messageType,
            assemblyResolver: assemblyName =>
            {
                if (assemblyName.Name == "SamplePhase1")
                {
                    Console.WriteLine("Message received from SamplePhase1 assembly, changing to SamplePhase2 assembly");

                    return AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName("SamplePhase2"));
                }

                return AssemblyLoadContext.Default.LoadFromAssemblyName(assemblyName);
            },
            typeResolver: (assembly, typeName, ignoreCase) =>
            {
                if (typeName == "CreateOrderPhase1")
                {
                    Console.WriteLine("CreateOrderPhase1 received, changing to CreateOrderPhase2");

                    return typeof(CreateOrderPhase2);
                }

                if (assembly != null)
                {
                    return assembly.GetType(typeName);
                }

                return Type.GetType(typeName);
            },
            throwOnError: true);

        if (type == null)
        {
            throw new Exception($"Could not determine type: {messageType}");
        }

        headers[messageTypeKey] = type.AssemblyQualifiedName;

        return Task.CompletedTask;
    }
}

Running the sample

When the sample is run both endpoints will startup. The Phase1 endpoint will send a CreateOrderPhase1 message to Phase2. Phase2 will then mutate the message into a CreateOrderPhase2 and handle the message.

Samples

Related Articles

  • Serialization
    .NET messaging systems require serialization and deserialization of objects sent/received over transports. NServiceBus achieves this using serializers.