【问题标题】:Using Ninject to find appropriate CommandHandler使用 Ninject 找到合适的 CommandHandler
【发布时间】:2015-12-01 17:58:32
【问题描述】:

我正在尝试使用带有类似于 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;
    }
}

我在具有此配置的 Worker 角色中使用 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 应该为给定的 Command 找到 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);
    }

【问题讨论】:

    标签: c# generics ninject


    【解决方案1】:

    让我编写与您发布的代码相同的代码,但“格式”略有不同:

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

    所以不是调用ICommandDispatcher.Dispatch&lt;commandType&gt;(command),而是使用类型参数ICommand 调用它。需要使用反射为Dispatch&lt;TParameter&gt;(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;
    }
    

    【讨论】:

    • 非常感谢您抽出宝贵时间回复。我知道我必须使用 MakeGenericMethod 但不确定如何。我已更改我的代码以符合您的建议,但现在出现错误:“无法从用法中推断方法 'CommandProcessor.GetMethod(Expression>)' 的类型参数。请尝试指定类型显式参数。”
    • @JohnMc 抱歉,我已经凭记忆输入了大部分代码。现在应该修复错误:GetMethod&lt;ICommand&gt;(c =&gt; _commandDispatcher.Dispatch(c))。希望没有更多。
    • 尚未测试,但正在编译。谢谢,如果可以的话,我会给你一百个 +1!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-19
    • 1970-01-01
    • 2019-03-24
    • 1970-01-01
    • 2013-05-23
    相关资源
    最近更新 更多