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

Bryan Murphy bmurphy1976 at gmail.com
Thu May 5 15:13:34 BST 2011


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-rabbitmq.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)
 	etc.

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
	.ToRoute("routeName")
	.WithHandler(msg => { do something });

or

// 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);
// }

subscription.Start();
subscription.Stop();

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/QueueingConsumerFactory.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.

Bryan


More information about the rabbitmq-discuss mailing list