I'm having some problems setting up a command handling architecture. I want to be able to create a number of different commands derived from ICommand; then, create a number of different command handlers derived from ICommandHandler;
Here's the interface and classes I have begun to define:
interface ICommand {}
class CreateItemCommand : ICommand {}
interface ICommandHandler<TCommand> where TCommand : ICommand {
void Handle(TCommand command);
}
class CreateItemCommandHandler : ICommandHandler<CreateItemCommand> {
public void Handle(CreateItemCommand command) {
// Handle the command here
}
}
I have a helper class that can create the appropriate type of command:
class CommandResolver {
ICommand GetCommand(Message message) {
return new CreateItemCommand(); // Handle other commands here
}
}
And, a helper class that creates the appropriate handler; this is where I'm having trouble:
class CommandHandlerResolver {
public ICommandHandler<TCommand> GetHandler<TCommand>(TCommand command) {
// I'm using Ninject and have an instance of an IKernel
// The following code throws an exception despite having a proper binding
// _kernel.GetService(typeof(ICommandHandler<TCommand>))
var bindingType = typeof(ICommandHandler<>).MakeGenericType(command.GetType());
var handler = _kernel.GetService(bindingType);
return handler as ICommandHandler<TCommand>;
// handler will be null after the cast
}
}
Here's the main running method
CommandResolver _commandResolver;
HandlerResolver _handlerResolver;
void Run() {
// message is taken from a queue of messages
var command = _commandResolver.GetCommand(message);
var handler = _handlerResolver.GetHandler(command);
// handler will always be null
handler.Handle(command);
}
I can think of a few different ways to refactor the code that I'm sure would avoid the issue, but I found myself a bit perplexed by the problem and wanted to understand more of what was going on.
This design looks like it should work.
Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types.
In C#, covariance and contravariance enable implicit reference conversion for array types, delegate types, and generic type arguments. Covariance preserves assignment compatibility and contravariance reverses it.
Covariance can be translated as "different in the same direction," or with-different, whereas contravariance means "different in the opposite direction," or against-different. Covariant and contravariant types are not the same, but there is a correlation between them. The names imply the direction of the correlation.
Covariance permits a method to have return type that is more derived than that defined in the delegate. Contravariance permits a method that has parameter types that are less derived than those in the delegate type.
Your problem is that you're mixing static types and runtime types: you're writing code that relies on having constructed generic types, but then you're invoking it with the base interface types.
Let's follow through your main flow:
Your CommandResolver
always returns the static type ICommand
. When you say:
var command = _commandResolver.GetCommand(message);
var handler = _handlerResolver.GetHandler(command);
The type of command
is bound to ICommand
and then gets passed to GetHander
, which invokes GetHandler<ICommand>
. That is, TCommand
in this invocation is always bound to ICommand
.
This is the main problem here. Since TCommand
is always ICommand
, doing:
_kernel.GetService(typeof(ICommandHandler<TCommand>))
...doesn't work (it looks for a ICommandHandler<ICommand>
and the kernel doesn't have it); and even if it did work, you'd have to return it as ICommandHandler<ICommand>
since that's the return type of the method.
By calling GetHandler
without knowing (at compile time) the real type of the command, you lost the ability to use generics effectively and TCommand
becomes meaningless.
So, you try to work around this: your resolver uses the command's runtime type(command.GetType()
) to reflectively construct the type ICommandHandler<SomeCommandType>
and tries to find that in the kernel.
Assuming you have something registered for that type, you'll get an ICommandHandler<SomeCommandType>
, which you'll then try to cast to ICommandHandler<ICommand>
(remember that TCommand
is bound to ICommand
). This of course won't work, unless TCommand
is declared covariant in ICommandHandler<TCommand>
, since you're casting up the type hierarchy; but even if it did, that's not what you want because what would you do with a ICommandHandler<ICommand>
anyway?
Simply put: you can't cast an ICommandHandler<SomeCommand>
to a ICommandHandler<ICommand>
because that would imply that you can pass it any kind of ICommand
and it'll happily handle it -- which is not true. If you want to use generic type parameters, you'll have to keep them bound to the real command type throughout the entire flow.
One solution to this problem is to keep TCommand
bound to the real command type throughout the resolution of both the command and the command handler, e.g. by having something like FindHandlerAndHandle<TCommand>(TCommand command)
and invoking it by reflection using the command's runtime type. But this is smelly and clumsy, and for a good reason: you're abusing generics.
Generic type-parameters are meant to help you when you know, in compile time, the type you want, or what you can unify it with another type parameter. In cases such as these, where you don't know that runtime type, trying to use generics only gets in your way.
A cleaner way to solve this is by separating the context when you know the command's type (when you write a handler for it) from the context when you don't know it (when you try to generically find a handler for a generic command). A good way to do that is by using an "untyped interface, typed base class" pattern:
public interface ICommandHandler // Look ma, no typeparams!
{
bool CanHandle(ICommand command);
void Handle(ICommand command);
}
public abstract class CommandHandlerBase<TCommand> : ICommandHandler
where TCommand : ICommand
{
public bool CanHandle(ICommand command) { return command is TCommand; }
public void Handle(ICommand command)
{
var typedCommand = command as TCommand;
if (typedCommand == null) throw new InvalidCommandTypeException(command);
Handle(typedCommand);
}
protected abstract void Handle(TCommand typedCommand);
}
This is a common way to bridge the generic and non-generic worlds: you use the non-generic interfaces when invoking them, but take advantage of the generic base-class when implementing. Your main flow now looks like this:
public void Handle(ICommand command)
{
var allHandlers = Kernel.ResolveAll<ICommandHandler>(); // you can make this a dependency
var handler = allHandlers.FirstOrDefault(h => h.CanHandle(command));
if (handler == null) throw new MissingHandlerException(command);
handler.Handle(command);
}
This is also somewhat more robust in the sense that the actual runtime type of the command doesn't have to match one-by-one with the type of the handler, so if you have a ICommandHandler<SomeBaseCommandType>
it can handle commands of type SomeDerivedCommandType
, so you can build handlers for intermediate base classes in the command type hierarchy, or use other inheritance tricks.
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