The interfaces,commands and command handler set up as per instructions in Simpleinjector wiki.
public interface ICommand
{
string Name { get; set; }
}
public class Command1 : ICommand
{
public string Name { get; set; }
}
public class Command2 : ICommand
{
public string Name { get; set; }
}
public interface ICommandHandler<TCommand>
{
void Execute(TCommand Command);
}
public class Command1Handler : ICommandHandler<Command1>
{
public void Execute(Command1 Command) {
Console.WriteLine(Command.Name);
}
}
public class Command2Handler : ICommandHandler<Command2>
{
public void Execute(Command2 Command) {
Console.WriteLine(Command.Name + "Hello");
}
}
Decorator:
public class CommandDecorator<TCommand> : ICommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> _handler;
public CommandDecorator(ICommandHandler<TCommand> handler)
{
this._handler = handler;
}
public void Execute(TCommand command)
{
this._handler.Execute(command);
}
}
Sample program
public class Program
{
static void Main(string[] args)
{
Container container = new Container();
//registering
container.RegisterAll<ICommand>(typeof(Command1), typeof(Command2));
container.RegisterManyForOpenGeneric(
typeof(ICommandHandler<>),
typeof(ICommandHandler<>).Assembly);
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(CommandDecorator<>));
container.Verify();
// sample test command
ICommand testcommand = new Command2();
testcommand.Name = "command 1";
var type = typeof(ICommandHandler<>).MakeGenericType(testcommand.GetType());
dynamic instance = container.GetInstance(type);
instance.Execute((dynamic)testcommand);
}
}
Is this the right way to get the right handler for handling the command at runtime. This is a sample and in the real app the commands are going to posted to a queue and a service is going to read the command and process it . I guess the Decorator has to be used for that but am not able to get it working. please suggest better options if any.
Your commands (Command1
and Command2
) are not services: they should not be registered. They are runtime data (message) that you pass through your services (your command handlers). So you should remove the Collection.Register<ICommand>
(RegisterAll
in v2) registration. It is of no use. You already see its of no use, since in your example you are newing the Command2
up manually, which is the right thing to do.
What you are doing in the last three lines of code is dispatching a command of an unknown type to the right command handler registration. You always need some reflection to pull this of, since you need to build the closed ICommandHandler<TCommand>
type based on the command type, which is something you don't know at compile time. Instead of using the C# dynamic
keyword, you can also use the .NET reflection API, but in my experience using dynamic
is better in this particular case. One important downside of the reflection API is that the API will always wrap any thrown exception (in case of a failure) with an InvocationException
and that makes it harder to do certain exception handling up the call stack.
So long story short, this should be your registration:
Container container = new Container();
container.Register(
typeof(ICommandHandler<>),
typeof(ICommandHandler<>).Assembly);
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(CommandDecorator<>));
And this should be the dispatching logic:
var type = typeof(ICommandHandler<>).MakeGenericType(command.GetType());
dynamic handler = container.GetInstance(type);
handler.Execute((dynamic)command);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With