使用Ninject查找合适的CommandHandler

本文关键字:CommandHandler Ninject 查找 使用 | 更新日期: 2023-09-27 18:24:41

我正在尝试使用与CQRS中使用的命令模式类似的Azure消息队列。

下面是一个命令示例:

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对象,它被放置在Azure消息队列上:

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; }
}

以下是(未优化的)代码,它试图取消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,那么它可以按预期工作。这显然是不可接受的,我认为如果我有一个Object和一个完全限定的类名,就不会太难进行强制转换。

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);
    }

使用Ninject查找合适的CommandHandler

让我写你发布的相同代码,但"格式"有点不同:

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;
}