A while back I moaned that ICommand was nice – but was a real pain when trying to fit it into a SOLID based app, using DI.
As a reminder, the command pattern is:
public interface ICommand {
event CanExecuteHasChanged;
//Takes no args, returns no args.
void Execute();
//Rollback -- when possible.
void Unexecute();
bool CanExecute();
bool CanUnexecute();
}
And the implementation of it could be:
public class LogMessage : ICommand {
...
public LogMessage(ILoggingService loggingService, string message){...}
public void Execute()
{
_loggingService.Log(message);
}
...
}
The problem with the above – when trying to use the Command pattern in a DI based app – is of course the constructor.
In the traditional def of the Command pattern (and there are different schools of thought as to what is the right def of a Command…) the Constructor is used to pass in the arguments required for the Execute method to not need additional arguments.
As you can imagine, this mucks up the machinery of most IoC’s which are geared to push in service dependencies, not arguments (at least without a lot of work).
A set of pattern that comes to the rescue is the ICommandHandler + ICommandMessage patterns.
public interface ICommandResponse {
bool Success {get;}
bool Message {get;}
IEnumerable<Exception> Exceptions {get;}
}
public interface ICommandMessage {
}
public interface ICommandMessageHandler<in TCommandMessage>
where TCommandMessage : ICommandMessage {
//Note: action is separated from command args:
ICommandResult Execute(TCommandMessage commandMessage);
}
public interface ICommandDispatcher {
ICommandResult Execute<TCommandMessage>(TCommandMessage commandMessage)
where TCommandMessage: ICommandMessage;
//IEnumerable<Exception> Validate<TCommandMessage>(TCommandMessage commandMessage)
//where TCommandMessage : ICommandMessage;
}
Ignoring the ICommandDispatcher (sometimes called ICommandBus) for a second, let’s look at what’s been defined.
What we are doing here is separating the Args required for execution from the Service handler.
This means that on one hand, the command message/data can be serialized, and on the other, the Executor / Handler can be DI’ed with services as required. Solves that problem.
As an aside, notice also the use of a return contract that has some legs, (certainly better than void…although I do like void…) allowing for some more robust messaging of exceptions across the app stack.
About nomenclature: Note that many developers call the above ICommandMessage ICommandData – or worse ICommand. I think both of these names are inappropriate, For one the word Command implies execution – which an argument package obviously is not. I don’t have anything against Data…it is appropriate, but I prefer Message, as it implies the ability to cross layers and tiers, and be executed where ever the Handler is located. Just seems more…future proof. But don’t be surprised if in many cases you see it called ICommand, that’s all.
Anyway – with the above separation of Message from Function, we can rewrite our log command as something like:
//Serializable
public interface ILogCommandMessage : ICommandMessage
public class LogCommandMessage : ILogCommandMessage{
public string Message {get;set;}
}
public class LogCommandMessageHandler : ICommandMessageHandler<ILogCommandMessage>{
ILoggingService _loggingService;
public LogCommandMessageHandler(ILoggingService loggingService){
_loggingService = loggingService;
}
public ICommandResponse Execute(){
_loggingService.Log(msg);
}
}
public class CommandDispatcher : ICommandDispatcher {
public ICommandResult Execute<TCommandMessage>(TCommandMessage commandMessage)
where TCommandMessage : ICommandMessage {
//Let the bus find the Handler appropriate
//for the given command:
ICommandMessageHandler<TCommandMessage> commandMessageHandler =
_serviceLocator.Current.GetService<ICommandMessageHandler<TCommandMessage>>();
//and execute it:
return commandMessageHandler.Execute(command);
}
}
Et voila…
Links:
http://bit.ly/sYsAMZ
http://bit.ly/sDXWFP