Download source code from: GitHub.
It is hard to ignore Dependency Injection in software development these days, since all the cool kids are bragging about it. In this article I use Ninject, because it has better support from developer community due to its large adoption. So I can seek out for help quickly.
However when it come to dealing with Generic Interface, we need to tell Ninject what Generic Interface bind to what Concrete Class. Just like the following syntax:
However when it come to dealing with Generic Interface, we need to tell Ninject what Generic Interface bind to what Concrete Class. Just like the following syntax:
this.kernel = new StandardKernel();
this.kernel.Bind<IMessageHandler<PersonMessage>>().To<PersonMessageHandler>(); this.kernel.Bind<IMessageHandler<CoffeeMessage>>().To<CoffeeMessageHandler>();
The problem is when you add more Message type and its Message Handler, you need to add extra code to configure the new messages and it handlers. I am a lazy developer, I want to write the code that is agnostic enough and will always work without code changing.
Here is what you need to do: Install these 2 NuGet Packages:
Here is what you need to do: Install these 2 NuGet Packages:
- using Ninject;
- using Ninject.Extensions.Conventions;
this.kernel.Bind(x => x.FromThisAssembly() .SelectAllClasses().InheritedFrom(typeof(IMessageHandler<>)) .BindAllInterfaces());
Basically it tell Ninject's configuration to find all Class that inherited from Generic Interface IMessageHandler<> then register all of them.
Let take one step back and look what I am trying to do with this small project.
I need to process a collection of different type of message. Each type has it own handler class.
Here are two types of message: PersonMessage and CoffeeMessage, that both are inherited from BaseMessage class.
I need to process a collection of different type of message. Each type has it own handler class.
Here are two types of message: PersonMessage and CoffeeMessage, that both are inherited from BaseMessage class.
public class BaseMessage
{
public string Name { get; set; }
}
public class PersonMessage : BaseMessage
{
public int Age { get; set; }
}
public class CoffeeMessage : BaseMessage
{
public int Volume { get; set; }
}
Here are the two message handlers: PersonMessageHandler and CoffeeMessageHandler, that both are implements Generic Interface IMessageHandler<T>
public interface IMessageHandler<T> where T : BaseMessage
{
void Handle(T message);
}
public class PersonMessageHandler : IMessageHandler<PersonMessage>
{
public void Handle(PersonMessage message)
{
//Do something useful with the person message here
}
}
public class CoffeeMessageHandler : IMessageHandler<CoffeeMessage>
{
public void Handle(CoffeeMessage message)
{
//Do something useful with the coffee message here
}
}
This is where system process the message. The code work fine, but it just fall into the same problem as mentioned above. Every time I add another message type I need to change the code that invoke the Handle method for each message.
var messageList = new List<BaseMessage>(); messageList.Add(new PersonMessage() { Name = "Jame", Age = 20 }); messageList.Add(new CoffeeMessage() { Name = "Mocha", Volume = 1 }); messageList.Add(new PersonMessage() { Name = "Ken", Age = 15 }); messageList.Add(new CoffeeMessage() { Name = "Cappuccino", Volume = 1 }); messageList.Add(new PersonMessage() { Name = "Tim", Age = 35 }); messageList.Add(new CoffeeMessage() { Name = "Late", Volume = 1 }); foreach (var message in messageList)
{
if(message is PersonMessage)
{
var handler = this.kernel.Get<IMessageHandler<PersonMessage>>();
handler.Handle(message as PersonMessage);
}
else if(message is CoffeeMessage)
{
var handler = this.kernel.Get<IMessageHandler<CoffeeMessage>>();
handler.Handle(message as CoffeeMessage);
}
}
Here is the agnostic code to process what ever message I will add in the future.
this.ProcessMessage(message);
public void ProcessMessage<T>(T message) where T : BaseMessage
{
// Mam Baa Jam Baa code to construct the right message handler type
Type handlerType = typeof(IMessageHandler<>);
Type[] typeArgs = { message.GetType() };
Type constructed = handlerType.MakeGenericType(typeArgs);
// Here we are the Chosen One message handler
var handler = this.kernel.Get(constructed);
// Invoke handle message
var methodInfo = constructed.GetMethod("Handle");
methodInfo.Invoke(handler, new[] { message });
}