2015-11-27 35 views
1

我正在嘗試使用類似於CQRS中使用的命令模式的Azure消息隊列。使用Ninject查找適當的CommandHandler

下面是一個例子命令:

public class SetZoneModeCommand : ICommand 
{ 
    public string GatewayId { get; set; } 

    public string ReceiverId { get; set; } 

    public int ChannelNumber { get; set; } 

    public HeatingMode Mode { get; set; } 
} 

這裏是它的處理器

public class SetZoneModeCommandHandler : ICommandHandler<SetZoneModeCommand> 
{ 
    private readonly IDatabaseContext _databaseContext; 

    public SetZoneModeCommandHandler(IDatabaseContext databaseContext) 
    { 
     _databaseContext = databaseContext; 
    } 

    public RequestStatus Execute(SetZoneModeCommand command) 
    { 
     if (command == null) 
     { 
      throw new ArgumentNullException("command"); 
     } 
     var result = new RequestStatus(); 

     return result; 
    } 
} 

我在這個配置輔助角色使用Ninject:

_kernel.Bind(x => x.FromAssembliesMatching("Business.dll") 
      .SelectAllClasses() 
      .BindDefaultInterface()); 

該作品罰款和依賴正在被注入。

我有其使用JSON序列化並且這被放置在天青消息隊列QueuedCommand對象:

public class QueuedCommand 
{ 
    public string ClassName { get; set; } 

    public object Command { get; set; } 

    public DateTime AddedOn { get; set; } 

    public int AddedByUserId { get; set; } 

    public int RetryCount { get; set; } 
} 

這裏是(未優化)碼,其試圖deserialise的QueueCommand並對其進行處理:

var queuedCommand = (QueuedCommand)JsonConvert.DeserializeObject<QueuedCommand>(message.AsString); 
      var commandInterface = typeof(ICommand); 

      var commandType = (from assembly in AppDomain.CurrentDomain.GetAssemblies() 
       from type in assembly.GetTypes() 
       where (commandInterface.IsAssignableFrom(type)) && (commandInterface != type) 
         && type.FullName == queuedCommand.ClassName 
       select type).FirstOrDefault(); 

var o = (JObject) queuedCommand.Command; 
       var command = (ICommand)o.ToObject(commandType); 
       var result = _commandDispatcher.Dispatch(command); 

這一切工作正常,如果我調試,傳遞給調度員的命令對象是正確的類型,並填充預期值。

CommandDispatcher應該爲它給出的命令找到CommandHandler的具體實現。我的問題是,它不是,我收到一個關於ICommandHandler沒有綁定的錯誤。

如果我從ICommand中替換強制轉換爲SetZoneModeCommand,那麼它將按預期工作。這顯然是不可接受的,我認爲如果我有一個對象和一個完全合格的類名,就不會太難。

public interface ICommandDispatcher 
{ 
    /// <summary> 
    /// Dispatches a command to its handler 
    /// </summary> 
    /// <typeparam name="TParameter">Command Type</typeparam> 
    /// <param name="command">The command to be passed to the handler</param> 
    RequestStatus Dispatch<TParameter>(TParameter command) where TParameter : ICommand; 
} 

public CommandDispatcher(IKernel kernel) 
    { 
     if (kernel == null) 
     { 
      throw new ArgumentNullException("kernel"); 
     } 
     _kernel = kernel; 
    } 

    public RequestStatus Dispatch<TParameter>(TParameter command) where TParameter : ICommand 
    { 
     var handler = _kernel.Get<ICommandHandler<TParameter>>(); 
     return handler.Execute(command); 
    } 

回答

2

讓我給你寫貼了稍微不同的「格式化」相同的代碼:

ICommand command = (ICommand)o.ToObject(commandType); 
RequestStatus result = _commandDispatcher.Dispatch<ICommand>(command); 

所以不是叫ICommandDispatcher.Dispatch<commandType>(command)你與類型參數ICommand調用它。你需要使用反射來選擇Dispatch<TParameter>(TParameter command)正確的類型參數:


object command = o.ToObject(commandType); 

MethodInfo dispatchMethod = GetMethod<ICommand>(c => _commandDispatcher.Dispatch(c)) 
    .GetGenericMethodDefinition() 
    .MakeGenericMethod(commandType); 

RequestStatus result = (RequestStatus)dispatchMethod.Invoke(
    _commandDispatcher, 
    new object[] { command }); 


public static MethodInfo GetMethod<T1>(Expression<Action<T1>> methodSelector) 
{ 
    return GetMethodInfo(methodSelector); 
} 

private static MethodInfo GetMethodInfo(LambdaExpression methodSelector) 
{ 
    if (methodSelector == null) 
    { 
     throw new ArgumentNullException("methodSelector"); 
    } 
    if (methodSelector.Body.NodeType != ExpressionType.Call) 
    { 
     throw new ArgumentOutOfRangeException(
      "methodSelector", 
      "Specified expression does is not a method call expression."); 
    } 

    var callExpression = (MethodCallExpression)methodSelector.Body; 
    return callExpression.Method; 
} 
+0

非常感謝您在百忙之中回覆時間。我知道我必須使用MakeGenericMethod,但不知道如何。我已經改變了我的代碼以符合你的建議,但是現在出現錯誤:「方法'CommandProcessor.GetMethod (Expression >)'的類型參數不能從使用推斷出來,請嘗試明確指定類型參數。 –

+1

@JohnMc對不起,我已經按內存鍵入了大部分代碼。現在應該修正錯誤:'GetMethod (c => _commandDispatcher.Dispatch(c))''。希望沒有更多。 – BatteryBackupUnit

+0

尚未測試,但現在正在編譯。謝謝,如果可以的話,我會給你一百+ 1! –