Routemeister is a small NuGet built with one single purpose. Assisting with in-process async message routing. It can be used if you e.g. are dispatching messages cross process using RabbitMQ or ActiveMQ and then want to dispatch the message to typed handlers within the consuming process.
For latest up to date info, visit the project page at GitHub.
Usage
First, install it. It's distributed via NuGet.
install-package routemeister
Now, you need to define a message handler marker interface. The requirements are:
- Generic interface, with one generic argument (name it what ever you want)
- Should contain a single method (name it what ever you want)
- The method should accept one argument only (the message)
- The method should return a
Task
.
//This interface is something you define and
//not part of RouteMeister.
public interface IHandle<T>
{
Task HandleAsync(T message);
}
Now, create some actual handlers (classes implementing your interface) that is supposed to process your messages. Each class can naturally implement the interface multiple times to handle different types of messages.
//Implement your interface multiple times
//to handle many types of messages
public class MyHandler :
IHandle<MyConcreteMessage>,
IHandle<ISomeInterfaceMessage>
{
public Task HandleAsync(MyConcreteMessage message)
{
//Do something with the message
}
//You can route messages as interfaces as well
public Task HandleAsync(ISomeInterfaceMessage message)
{
//Do something with the message
}
}
Each message type being processed can (if you want) be processed by multiple message handlers (different classes).
//You can have many classes handling the message types
//by implementing your interface in yet another class
public class SomeOtherHandler :
IHandle<MyConcreteMessage>,
IHandle<ISomeInterfaceMessage>
{
public Task HandleAsync(MyConcreteMessage message)
{
//Do something with the message
}
public Task HandleAsync(ISomeInterfaceMessage message)
{
//Do something with the message
}
}
Now, lets get the routes constructed. A route is represented by MessageRoute
. Each message route has a MessageType
and one-to-many Actions
. Each Action
represents a message handler.
To get the message routes, you use the MessageRouteFactory
. To the factory, you pass a Func<Type, object>
that is responsible for creating an instance of the message handler class. Why? Well, you could pass Activator.CreateInstance
but you probably want to use your IoC container or something, so that additional dependencies are resolved via the container.
//Using Activator
var factory = new MessageRouteFactory(
type => Activator.CreateInstance(type));
//Using Ninject
var factory = new MessageRouteFactory(
type => kernel.Get(type));
The factory needs to know in what assemblies to look for message handlers. It also needs to know which interface is used as the marker interface.
MessageRoutes routes = factory.Create(
typeof(SomeType).Assembly,
typeof(IHandle<>));
Now, the routes
can be used as you want to route messages manually or using e.g. SequentialAsyncMessageRouter
.
var router = new SequentialAsyncMessageRouter(routes);
await router.RouteAsync(new MyConcreteMessage
{
//Some data
}).ConfigureAwait(false);
or manually
var message = new MyConcreteMessage
{
//Some data
};
var messageType = message.GetType();
var route = routes.GetRoute(messageType);
foreach (var action in route.Actions)
await action(message).ConfigureAwait(false);
That's it. Use if it helps. Contribute if you have any ideas for improvement.
Cheers,
//Daniel