When you consume messages from EasyNetQ bus you do not need to worry about naming and creating queues or exchanges. That’s done for you. The framework will use a pattern when generating name for the queue which is based on strongly typed name of the message type and consumer’s subscription id. Similar happens when generating exchange name. But is there anything you can do to change this behaviour?

Using Advanced bus

EasyNetQ gives you an Advanced bus which allows to manually create and manage queues, exchanges and bindings. You can use it to control queue names but it may be an overkill if you want only to control names.

IConventions

When the bus has to resolve queue or exchange name it is using an instance of IConventions type. The standard implementation takes strongly typed name of the message type and appends subscriber’s subscriptionId to the end. By providing own implementation you can control the way exchange and queue names are generated.

For full flexibility of use I decided on attributes which can be used to decorate message type and specify name of the exchange and/or queue to be used. Below example shows TestMessage class for which we want to use queue named TestMessageQueue and exchange named TestExchange:

[ExchangeName("TestExchange"), QueueName("TestMessageQueue")]
public class TestMessage
{
// ...
}

To use attribute based conventions you have to register it’s implementation with the bus:

var bus = RabbitHutch.CreateBus(
"host=localhost",
x => x.Register<IConventions, AttributeBasedConventions>());

The AttributeBasedConventions implementation is quite simple. When generating exchange or queue name it checks whether the message type has an attribute. If yes then it takes the name from the attribute, if no then it uses classic implementation. You can find the code at the end of this post.

Registering consumer

When registering consumer you can pass an empty string as subscriptionId parameter. This will use value specified in the attribute as a queue name. If you provide a subscriptionId than it will be appended to the end. You can read more about using subscription ids in the EasyNetQ – methods for delivering messages post.

Adding QueueNameAttribute to a running system

If you have running system and would like to apply QueueNameAttribute or ExchangeNameAttribute to your message type then bear in mind that you have to manually migrate existing messages to new queues.

Code

using EasyNetQ;
using System;
using System.Linq;

namespace EasyNetQSpike.AttributeConventions
{
    public class AttributeBasedConventions : Conventions
    {
        private readonly ITypeNameSerializer _typeNameSerializer;

        public AttributeBasedConventions(ITypeNameSerializer typeNameSerializer)
            : base(typeNameSerializer)
        {
            if (typeNameSerializer == null)
                throw new ArgumentNullException("typeNameSerializer");

            _typeNameSerializer = typeNameSerializer;

            ExchangeNamingConvention = GenerateExchangeName;
            QueueNamingConvention = GenerateQueueName;
        }

        private string GenerateExchangeName(Type messageType)
        {
            var exchangeNameAtt = messageType.GetCustomAttributes(typeof(ExchangeNameAttribute), true).SingleOrDefault() as ExchangeNameAttribute;

            return (exchangeNameAtt == null)
                ? _typeNameSerializer.Serialize(messageType)
                : exchangeNameAtt.ExchangeName;
        }

        private string GenerateQueueName(Type messageType, string subscriptionId)
        {
            var queueNameAtt = messageType.GetCustomAttributes(typeof(QueueNameAttribute), true).SingleOrDefault() as QueueNameAttribute;

            if (queueNameAtt == null)
            {
                var typeName = _typeNameSerializer.Serialize(messageType);
                return string.Format("{0}_{1}", typeName, subscriptionId);
            }

            return string.IsNullOrEmpty(subscriptionId)
                ? queueNameAtt.QueueName
                : string.Concat(queueNameAtt.QueueName, "_", subscriptionId);
        }
    }
}

EasyNetQSpike.AttributeConventions.AttributeBasedConventions.cs

using System;

namespace EasyNetQSpike.AttributeConventions
{
    [Serializable]
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class ExchangeNameAttribute : Attribute
    {
        public ExchangeNameAttribute(string exchangeName)
        {
            ExchangeName = exchangeName;
        }

        public string ExchangeName { get; set; }
    }
}

EasyNetQSpike.AttributeConventions.ExchangeNameAttribute.cs

using System;

namespace EasyNetQSpike.AttributeConventions
{
    [Serializable]
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class QueueNameAttribute : Attribute
    {
        public QueueNameAttribute(string queueName)
        {
            QueueName = queueName;
        }

        public string QueueName { get; set; }
    }
}

EasyNetQSpike.AttributeConventions.QueueNameAttribute.cs

namespace EasyNetQSpike.AttributeConventions
{
    [ExchangeName("TestExchange")]
    [QueueName("TestMessageQueue")]
    public class TestMessage
    {
        public int Id { get; set; }
        public string Text { get; set; }

        public override string ToString()
        {
            return string.Format("{0}: {1}", Id, Text);
        }
    }
}

EasyNetQSpike.AttributeConventions.TestMessage.cs