MassTransit. Consume equal objects defined in different namespaces
Asked Answered
S

2

8

First of all, excuse my English, it's very bad. I am using MassTransit with Azure Service Bus for asynchronous communication between microservices. By their own definition, and to avoid generating dependencies between them, messages sent between different microservices are defined in each of them, that is, they are part of different namespaces. The automatic management of MassTransit causes queues and topics to be managed by the object type, which prevents the microservices that consume a message from receiving the messages sent by the microservice publisher. The same thing happens with two classes with the same properties in the same namespace but with a different class name.

Is there any way to solve this? The options that have occurred to me are:

  • Remove the namespace from the endpoint of the destination address, naming it only with the name of the class.
  • That MassTransit can manage the creation of queues and topics based on the serialization of the object, instead of managing it based on the object type (perhaps through some type of wrapping object?)

I leave an example that I hope can help you in understanding the problem.

//FIRST PROGRAM - MESSAGE CONSUMER 

namespace Consumer
{
    public class Example
    {
        public string PropOne { get; set; }

        public string PropTwo { get; set; }
    }

    public class ExampleConsumer : 
        IConsumer<Example>
    {
        public List<Example> ConsumedTestObjectList { get; } = new List<Example>();

        //THIS METHOD NEVER CALL !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        public Task Consume(ConsumeContext<ExampleConsumer> context)
        {
            ConsumedTestObjectList.Add(context.Message);
            return Task.CompletedTask;
        }
    }

    public class ConsumerProgram
    {
        public static void Main()
        {
            var bus = Bus.Factory.CreateUsingAzureServiceBus(sbc =>
            {
                var host = sbc.Host("connectionString", h => {});

            });

            sbc.ReceiveEndpoint(host, e =>
            {
                e.Consumer<ConsumerProgram.Example>(context =>
                {
                    return Console.Out.WriteLineAsync($"Message Received: {JsonConvert.SerializeObject(context.Message)}");
                });
            });

            bus.Start(); // This is important!

            Console.WriteLine("Press any key to exit");
            Console.ReadKey();

            bus.Stop();
        }
    }
}


//SECOND PROGRAM - MESSAGE PUBLISHER 

namespace Publisher
{
    public class Example
    {
        public string PropOne { get; set; }

        public string PropTwo { get; set; }
    }

    public class PublisherProgram
    {
        public static void Main()
        {
            var bus = Bus.Factory.CreateUsingAzureServiceBus(sbc =>
            {
                var host = sbc.Host("connectionString", h => {});

            });

            bus.Start(); // This is important!

            //send new instance of Publisher.Example 
            var example = new Example() { PropOne = "1", PropTwo = "2" };
            bus.Publish(example);

            Console.WriteLine("Press any key to exit");
            Console.ReadKey();

            bus.Stop();
        }
    }
}

Thank you very much.

regards

Borja

Speech answered 24/9, 2018 at 10:24 Comment(1)
Messages are contracts and all communication using messages is contract-based. Type-based routing is the feature of MassTransit that embraces using typed messages as contracts and the routing happens based on the namespace and type name. If you consume a message of type Type A from Service A, it is natural for consumers to know that this is ServiceA.TypeA message, so they know where it comes from.Arlberg
C
18

The message type, and the resulting name, are a key concept within MassTransit. If you want to avoid sharing assemblies between projects, that is fine, but you will need to match the entire interface (or class, in your case) name, including namespace, or it will not route properly.

Yes, you can override the entity name formatter to change how topics are named but it won't change the message type requirement for deserialization of the message (which happens, by type).

So the recommendation here is to use the same namespace for the contracts, even if they're in separate projects.

Caviar answered 24/9, 2018 at 13:21 Comment(0)
N
1

Update in 2024

You can now set a message type different from the class name by using the MessageUrn attribute.

However, this doesn't affect the topic name in Azure Service Bus, which still uses the class name. To change the topic name, you also need to use the EntityName attribute.

To send and receive messages using C# types defined in different projects and namespaces, you need to use both the MessageUrn and EntityName attributes. The MessageUrn sets the message type, and the EntityName ensures the correct topic name is used in Azure Service Bus:

[MessageUrn("custom-message-type")]
[EntityName("custom-topic-name")]
public class Example
{
    public string PropOne { get; set; }

    public string PropTwo { get; set; }
}
Nogging answered 31/8 at 12:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.