11

I'm wondering if I'm doing something wrong, I expected MassTransit would automatically register ReceiveEndpoints in the EndpointConvention.

Sample code:

services.AddMassTransit(x =>
{
    x.AddServiceBusMessageScheduler();
    x.AddConsumersFromNamespaceContaining<MyNamespace.MyRequestConsumer>();
    x.UsingAzureServiceBus((context, cfg) =>
    {
        // Load the connection string from the configuration.
        cfg.Host(context.GetRequiredService<IConfiguration>().GetValue<string>("ServiceBus:ConnectionString"));
        cfg.UseServiceBusMessageScheduler();

        // Without this line I'm getting an error complaining about no endpoint convention for x could be found.
        EndpointConvention.Map<MyRequest>(new Uri("queue:queue-name"));

        cfg.ReceiveEndpoint("queue-name", e =>
        {
            e.MaxConcurrentCalls = 1;
            e.ConfigureConsumer<MyRequestConsumer>(context);
        });

        cfg.ConfigureEndpoints(context);
    });
});

I thought this line EndpointConvention.Map<MyRequest>(new Uri("queue:queue-name")); wouldn't be necessary to allow sending to the bus without specifing the queue name, or am I missing something?

await bus.Send<MyRequest>(new { ...});

Stephan
  • 2,356
  • 16
  • 38

1 Answers1

36

The EndpointConvention is a convenience method that allows the use of Send without specifying the endpoint address. There is nothing in MassTransit that will automatically configured this because, frankly, I don't use it. And I don't think anyone else should either. That stated, people do use it for whatever reason.

First, think about the ramifications - if every message type was registered as an endpoint convention, what about messages that are published and consumed on multiple endpoints? That wouldn't work.

So, if you want to route messages by message type, MassTransit has a feature for that. It's called Publish and it works great.

But wait, it's a command, and commands should be Sent.

That is true, however, if you are in control of the application and you know that there is only one consumer in your code base that consumes the KickTheTiresAndLightTheFires message contract, publish is as good as send and you don't need to know the address!

No, seriously dude, I want to use Send!

Okay, fine, here are the details. When using ConfigureEndpoints(), MassTransit uses the IEndpointNameFormatter to generate the receive endpoint queue names based upon the types registered via AddConsumer, AddSagaStateMachine, etc. and that same interface can be used to register your own endpoint conventions if you want to use Send without specifying a destination address.

You are, of course, coupling the knowledge of your consumer and message types, but that's your call. You're already dealing with magic (by using Send without an explicit destination) so why not right?

string queueName = formatter.Consumer<T>()

Use that string for the message types in that consumer as a $"queue:{queueName}" address and register it on the EndpointConvention.

Or, you know, just use Publish.

Chris Patterson
  • 28,659
  • 3
  • 47
  • 59
  • 1
    So to understand correctly, even though it is a command, if we where to use `Publish` instead of `Send` the consumers can stay the same and will automatically handle the messages? But we don't have to specify the endpoint conventions on the publisher side (separate website) – Stephan Jul 03 '20 at 12:49
  • 1
    Correct, if you have a single consumer for a message type, and publish, it's a 1:1 conversation without having to know the address. The message will be routed through a topic to the receive endpoint, where it will be consumed. – Chris Patterson Jul 03 '20 at 13:24
  • 2
    @ChrisPatterson Sorry for digging this up, but I get the feeling that *some* additional configuration is required with `Publish`. If I add the consumer, but don't register a receive endpoint, the message never gets consumed... – rumblefx0 Aug 05 '20 at 10:26
  • As long as your message type matches, including namespace, publish works on every transport. If you have questions, open a new one here with code or ask on Discord. – Chris Patterson Aug 05 '20 at 11:18
  • 4
    I think the semantics between `Send` and `Publish` are not the same if there is no subscriber: If I publish without subscribers, the message is discarded. `Send` would persist the message in a destination queue, even if there currently is no subscriber. – Alexander Groß Oct 07 '21 at 18:21
  • 4
    I'm a bit confused, because your official documentation says "Do not use Publish for commands" >> https://masstransit-project.com/usage/messages.html#message-names < – Marcus Kaseder Nov 05 '21 at 14:06
  • 4
    Well, the docs are wrong in this case. – Chris Patterson Nov 06 '21 at 13:15
  • @ChrisPatterson why not using `bus.GetPublishSendEndpoint()` to get send endpoint without manual uri? do I think true? – afruzan Jan 02 '22 at 13:13
  • What do you think `Publish` does? That gets you the endpoint by message type, which is exactly the same thing as publish. – Chris Patterson Jan 02 '22 at 14:02
  • 2
    As stated above, careful when making this decision. 1 - If the consumer bus is not started before the producer, the "command" published will be lost. 2 - If the "command" is published, you might have the same being processed multiple time if you have multiple consumers for the same message. 3 - You will get one additional exchange/topic for each "command" you publish. Please correct me if wrong. – Norcino Sep 12 '22 at 11:25
  • @ChrisPatterson are there any cost and performance implications of using Publish instead of Send? I know it depends on the transport used, but lets say we are using Azure ServiceBus. Thanks. – Gabriel Cerutti Jul 03 '23 at 09:40