I want to implement a CommandBus that can Dispatch some Commands to CommandHandlers.
- A
Commandis a simple a DTO describing what should happen. For instance : "Increment counter by 5" - A
CommandHandleris able to handle a precise type ofCommand. - The
CommandBustakes aCommandand executes theCommandHandlerthat is able to handle it.
The code I wrote does not compile.
Compiler complains cannot convert from 'IncrementHandler' to 'Handler<Command>'.
I don't understand why, because IncrementHandler implements Handler<Increment> and Increment implements Command
I've tried both in and out modifiers on the generic interfaces, it doesn't solve the problem.
Is there a way to achieve this with only interfaces ?
[TestClass]
public class CommandBusTest
{
[TestMethod]
public void DispatchesProperly()
{
var handler = new IncrementHandler(counter: 0);
var bus = new CommandBus(handler); // <--Doesn't compile: cannot convert from 'IncrementHandler' to 'Handler<Command>'
bus.Dispatch(new Increment(5));
Assert.AreEqual(5, handler.Counter);
}
}
public class CommandBus
{
private readonly Dictionary<Type, Handler<Command>> handlers;
public CommandBus(params Handler<Command>[] handlers)
{
this.handlers = handlers.ToDictionary(
h => h.HandledCommand,
h => h);
}
public void Dispatch(Command commande) { /*...*/ }
}
public interface Command { }
public interface Handler<TCommand> where TCommand : Command
{
Type HandledCommand { get; }
void Handle(TCommand command);
}
public class Increment : Command
{
public Increment(int value) { Value = value; }
public int Value { get; }
}
public class IncrementHandler : Handler<Increment>
{
// Handler<Increment>
public Type HandledCommand => typeof(Increment);
public void Handle(Increment command)
{
Counter += command.Value;
}
// Handler<Increment>
public int Counter { get; private set; }
public IncrementHandler(int counter)
{
Counter = counter;
}
}
paramsbecause I want to be able to pass as many Handlers as I want, like so :new CommandBus(new AHander(), new BHandler(), new CHandler())The bus will contain several Handlers, because there will be several types of Command. - Christophe Cadilhacparamssyntax is fine, it will appear as aHandler<Command>[]with one entry in the constructor. - Cee McSharpfaceHandler<TCommand>you can only make it contravariant inTCommandbut thenIncrementHandleris not compatible withHandler<Command>sinceCommandis a supertype ofIncrementand not a subtype as required. There's no way to do this safely so you'll have to cast somewhere. - Lee