dotnetcore / CAP

Distributed transaction solution in micro-service base on eventually consistency, also an eventbus with Outbox pattern
http://cap.dotnetcore.xyz
MIT License
6.6k stars 1.28k forks source link

It seems to be impossible to customize AMQP "subject" header when sending message to Azure Service Bus #1566

Open pawel-cyb opened 1 month ago

pawel-cyb commented 1 month ago

It seems that "subject" header from AMQP 1.0 (section 3.2.4) cannot be customized when using Azure Service Bus - it will always be the type name of the message body - because of two interacting pieces of code - one that prepares message for sending, and one that creates message producer based on config.

In ITransport.AzureServiceBus.cs the message subject is set based on message name given when publishing:

            var message = new ServiceBusMessage(transportMessage.Body.ToArray())
            {
                MessageId = transportMessage.GetId(),
                Subject = transportMessage.GetName(),
                CorrelationId = transportMessage.GetCorrelationId()
            };

When looking at the above code, it seems that it would be enough to use ICapPublisher.Publish function, which has a signature as pasted below, passing subject as first parameter:

    /// <summary>
    /// Publish an object message.
    /// </summary>
    /// <param name="name">the topic name or exchange router key.</param>
    /// <param name="contentObj">message body content, that will be serialized. (can be null)</param>
    /// <param name="headers">message additional headers.</param>
    void Publish<T>(string name, T? contentObj, IDictionary<string, string?> headers);

However, this is not the case. Why? Because this parameter cannot have arbitrary value - is required to always be equal to the dotnet type name of the message (something like message.GetType().Name).

Why only dotnet type name is accepted as message name? Because producer for message type is registered under a key which is a dotnet type name of the message - and when choosing producer for message, it is retrieved by message name given when publishing (p => p.MessageTypeName == transportMessage.GetName() from code below in ITransport.AzureServiceBus.cs - the same name that is later used as message subject.

    public IServiceBusProducerDescriptor CreateProducerForMessage(TransportMessage transportMessage)
    {
        return _asbOptions.Value
                   .CustomProducers
                   .SingleOrDefault(p => p.MessageTypeName == transportMessage.GetName())
               ??
               new ServiceBusProducerDescriptor(
                   transportMessage.GetName(),
                   _asbOptions.Value.TopicPath);
    }

As a result, subject is currently always equal to dotnet type of the message.

I also tried passing the headers parameter with "subject" key and custom value, however value passed in such way is not used for broker property - only additional custom property is added.

Would it be possible to add any way of customizing the message subject, or maybe correcting the way message producers are retrieved so that message name does not have to be equal to message type name?

yang-xiaodong commented 1 month ago

@pawel-cyb Thank you for your feedback.

Initially, we only supported a single topic. The contribution to support different producers for different topics comes from PR #1283. @mviegas is more familiar with this matter, and I believe he will provide an answer once he has time.

CC @jonekdahl