Skip to content

Instantly share code, notes, and snippets.

@philipmat
Last active July 22, 2017 13:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save philipmat/abaab9acc8d15867a5d7c80b69011fdc to your computer and use it in GitHub Desktop.
Save philipmat/abaab9acc8d15867a5d7c80b69011fdc to your computer and use it in GitHub Desktop.
Simple Message Routing: Static vs Dynamic
<Query Kind="Program">
<NuGetReference>Autofac</NuGetReference>
<Namespace>Autofac</Namespace>
</Query>
void Main()
{
var facker = Configure();
// example 1
var controller1 = facker.Resolve<ControllerOne>();
controller1.DoFoo();
controller1.DoBar();
// example 2
var controller2 = facker.Resolve<ControllerTwo>();
controller2.DoFoo();
controller2.DoBar();
// example 3
var controller3 = facker.Resolve<ControllerThree>();
controller3.DoFoo();
controller3.DoBar();
}
// Define other methods and classes here
private IContainer Configure()
{
var builder = new ContainerBuilder();
builder.RegisterType<FooMessageHandler>()
.As<IMessageHandler<FooMessage>>()
.As<IMessageHandler<IMessage>>();
builder.RegisterType<BarMessageHandler>()
.As<IMessageHandler<BarMessage>>()
.As<IMessageHandler<IMessage>>();
builder.RegisterType<DynamicHandler>();
builder.RegisterType<StaticHandler>();
builder.RegisterType<ControllerOne>();
builder.RegisterType<ControllerTwo>();
builder.RegisterType<ControllerThree>();
return builder.Build();
}
/// <summary>Gets injected with specific handlers.</summary>
public class ControllerOne
{
private IMessageHandler<FooMessage> _fooHandler;
private IMessageHandler<BarMessage> _barHandler;
public ControllerOne(IMessageHandler<FooMessage> fooHandler, IMessageHandler<BarMessage> barHandler)
{
_fooHandler = fooHandler; _barHandler = barHandler;
}
public void DoFoo()
{
var foo = new FooMessage { Id = 101 };
_fooHandler.Handle(foo);
}
public void DoBar()
{
var message = new BarMessage { Marker = "fortytwo" };
_barHandler.Handle(message);
}
}
/// <summary>Gets injected with a dynamic handler</summary>
public class ControllerTwo
{
protected IMessageHandler _handler;
public ControllerTwo(DynamicHandler handler) : this((IMessageHandler) handler) {}
protected ControllerTwo(IMessageHandler handler)
{
_handler = handler;
}
public void DoFoo()
{
var foo = new FooMessage { Id = 101 };
_handler.Handle(foo);
}
public void DoBar()
{
var message = new BarMessage { Marker = "fortytwo" };
_handler.Handle(message);
}
}
/// <summary>Gets injected with a static handler, but otherwise uses C2</summary>
public class ControllerThree : ControllerTwo
{
public ControllerThree(StaticHandler handler) : base((IMessageHandler) handler)
{
_handler = handler;
}
}
public interface IMessage
{
string Name { get; }
}
public class FooMessage : IMessage {
public string Name => "Foo";
public long Id { get; set; }
}
public class BarMessage : IMessage
{
public string Name => "Bar";
public string Marker { get; set; }
}
public interface IMessageHandler
{
void Handle(IMessage message);
}
public interface IMessageHandler<T> : IMessageHandler where T : IMessage { }
public abstract class MessageDispatcher<T> : IMessageHandler<T>
where T : IMessage
{
public void Handle(IMessage message)
{
$"Received {message.Name}.".Dump("MessageDispatcher<>");
InnerHandle((T) message);
}
protected abstract void InnerHandle(T message);
}
public class FooMessageHandler : MessageDispatcher<FooMessage>, IMessageHandler<IMessage>
{
protected override void InnerHandle(FooMessage message)
{
$"Got {message.Name}: {message.Id}".Dump(nameof(FooMessageHandler));
}
}
public class BarMessageHandler : MessageDispatcher<BarMessage>, IMessageHandler<IMessage>
{
protected override void InnerHandle(BarMessage message)
{
$"Got {message.Name}: {message.Marker}".Dump(nameof(BarMessageHandler));
}
}
/// <summary>Dynamically constructs handlers based on the underlying message type</summary>
public class DynamicHandler : IMessageHandler
{
ILifetimeScope _injector;
public DynamicHandler(ILifetimeScope injector)
{
_injector = injector;
}
public void Handle(IMessage message)
{
$"Dynamically handling: {message.Name}.".Dump(nameof(DynamicHandler));
Type messageType = message.GetType(); // reports actually type, e.g. FooMessage
using (var scope = _injector.BeginLifetimeScope())
{
Type handlerType = typeof(IMessageHandler<>).MakeGenericType(messageType);
var handler = scope.Resolve(handlerType) as IMessageHandler;
handler.Handle(message);
}
}
}
/// <summary>Loops through defined message handlers to find one whose underlying type matches the message type.</summary>
public class StaticHandler : IMessageHandler
{
private IEnumerable<IMessageHandler<IMessage>> _handlers;
public StaticHandler(IEnumerable<IMessageHandler<IMessage>> handlers)
{
_handlers = handlers;
}
public void Handle(IMessage message)
{
$"Statically handling: {message.Name}.".Dump(nameof(StaticHandler));
Type messageType = message.GetType(); // reports actually type, e.g. FooMessage
foreach (var handler in _handlers)
{
Type[] handlerInterfaceTypes = handler.GetType().GetInterfaces();
foreach (var interfaceType in handlerInterfaceTypes.Where(it => it.IsGenericType))
{
if (interfaceType.GetGenericArguments().Any(arg => arg == messageType))
{
handler.Handle(message);
return;
}
}
}
}
}
@philipmat
Copy link
Author

Output


MessageDispatcher<>
Received Foo. 

FooMessageHandler
Got Foo: 101 

MessageDispatcher<>
Received Bar. 

BarMessageHandler
Got Bar: fortytwo 



DynamicHandler
Dynamically handling: Foo. 

MessageDispatcher<>
Received Foo. 

FooMessageHandler
Got Foo: 101 

DynamicHandler
Dynamically handling: Bar. 

MessageDispatcher<>
Received Bar. 

BarMessageHandler
Got Bar: fortytwo 



@philipmat
Copy link
Author

Updated with a static handler, which is more faster, but clunky-er and consumes a lot more memory as all the handlers are instantiated prior to StaticHandler construction

@drlyday
Copy link

drlyday commented Jul 22, 2017

"You had me at": Type handlerType = typeof(IMessageHandler<>).MakeGenericType(message.GetType());

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment