[rabbitmq-discuss] My attempt at a high-level API for .NET

Mike Hadlow mike.hadlow at 15below.com
Thu May 5 15:50:32 BST 2011

Thanks Brian,

It's reassuring to hear that you are also successfully using QueueingBasicConsumer. I don't suppose you'd be willing to share your code?

Very interesting to hear that you are working with Mono. So I guess that means that the Rabbit .NET client is mono compatible. EasyNetQ should run well under mono too, I'll give it a try.


-----Original Message-----
From: Bryan Murphy [mailto:bmurphy1976 at gmail.com]
Sent: 05 May 2011 15:14
To: Mike Hadlow
Cc: rabbitmq-discuss at lists.rabbitmq.com
Subject: Re: [rabbitmq-discuss] My attempt at a high-level API for .NET

On Wed, May 4, 2011 at 11:15 AM, Mike Hadlow <mike.hadlow at 15below.com> wrote:
> I've just published details of a high level .NET API for Rabbit/AMQP:
> http://mikehadlow.blogspot.com/2011/05/easynetq-simple-net-api-for-rab
> bitmq.html

I've more or less gone through the same process as you.  I've been through a few iterations, and this is what I've settled on so far.

I have an IBus interface that is configured to send messages to a topic exchange, and then I create multiple instances of that class configured to multiple exchange (i.e. new Bus("host", "port", "exchange")).  The reason I do this is to de-emphasize exchanges and emphasize routes to reduce the cognitive burden for the rest of our team.  The whole exchange/route model gives us more flexibility than we need so I've simplified somewhat.

IBus looks like this:

variations of send:

        void Send(params object[] messages)
        void Send<TMessage>(params TMessage[] messages)
        void Send(IEnumerable<object> messages)
        void Send<TMessage>(IEnumerable<TMessage> messages)

a route method allowing explicit definition of the route:

        void Route(string routeName, params object[] messages)
        void Route(string routeName, IEnumerable<object> messages)
        void Route<TMessage>(string routeName, params TMessage[] messages)
        void Route<TMessage>(string routeName, IEnumerable<TMessage> messages)

async versions of all of the above:

        void SendAsync<TMessage>(IEnumerable<TMessage> messages)
        void RouteAsync<TMessage>(string routeName, params TMessage[] messages)

RPC requests:

        object SendAndReceive(Type responseType, object request)
        object SendAndReceive(Type responseType, object request, TimeSpan timeout)
        TResponse SendAndReceive<TRequest, TResponse>(TRequest request)
        TResponse SendAndReceive<TRequest, TResponse>(TRequest request, TimeSpan timeout)

        object RouteAndReceive(Type responseType, string routeName, object request)
        object RouteAndReceive(Type responseType, string routeName, object request, TimeSpan timeout)
        TResponse RouteAndReceive<TRequest, TResponse>(string routeName, TRequest request)
        TResponse RouteAndReceive<TRequest, TResponse>(string routeName, TRequest request, TimeSpan timeout)

IBus also has a RouteNameSelector property that references a class with one method:

        string SelectRouteName(BusConfig config, object message);

This can be swapped out for alternate implementations, but the default does the following when calling Send methods (this is not used for the Route methods):

        1. if message implements IRoutable, return IRoutable.RouteName
        2. if message has [RouteNameAttribute("RouteName")] return "RouteName"
        3. otherwise return message.GetType().Name

subscriptions look like this:

var subscription = bus.Subscribe
        .WithHandler(msg => { do something });


// where THandler : ConsumerOf<TMessage> var subscription = bus.Subscribe
        .ToQueue("queue name #1")
        .ToQueue("queue name #2")
        .WithContainerScopedHandler<TMessage, THandler>(container);

// equates for each message to:
// using (var childContainer = container.CreateLifeTimeContainer()) {
//      var handler = container.Resolve<THandler>();
//      handler.Consume(message);
// }


There are, of course, many overloads that allow me to consume messages in a variety of different ways (interface based, reflection method based, delegate based, etc. with and without scoping to a container) and additional fluent methods to control retries, timeouts, dead letter handling, and the likes.

> Feedback would be much appreciated. I'm especially hoping that someone
> will look at the way I'm consuming messages. Currently with a simple
> while loop in a new thread:
> https://github.com/mikehadlow/EasyNetQ/blob/master/EasyNetQ/QueueingCo
> nsumerFactory.cs
> I'm sure there must be a better way.

We use QueueingBasicConsumer and are quite happy with it.  I haven't had a need to use anything else.  Looking at our code, it's a little different, but most of that just has to do error handling and the additional code needed to support our abstractions.


 15below Limited: Company registered in England and Wales No 3945289
Registered Office: Lyndean House, 43-46 Queens Road, Brighton BN1 3XB, United Kingdom

15below Australia Pty Limited: ABN 25 132 716 379
Level 50, 120 Collins Street, Melbourne, Victoria 3000, Australia

Please think about the environment before printing this email.

This email and any attachments may be confidential and/or legally privileged and are solely for the use of the intended recipient.  If you have received this email in error please contact the sender.  Any views or opinions expressed within this e-mail are solely those of the sender, and do not necessarily represent those of 15below unless otherwise specifically stated.  Although 15below has taken every reasonable precaution to ensure that any attachment to this e-mail has been checked for viruses, it is strongly recommended that you carry out your own virus check before opening any attachment, as we cannot accept liability for any damage sustained as a result of software virus infection.

More information about the rabbitmq-discuss mailing list