使用 Ninject 找到合适的 CommandHandler
Using Ninject to find appropriate CommandHandler
我正在尝试将 Azure 消息队列与类似于 CQRS 中使用的命令模式一起使用。
这是一个示例命令:
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,那么它会按预期工作。这显然是不可接受的,我认为如果我有一个对象和一个完全限定的 class 名称,那么转换起来不会太困难。
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);
}
让我编写与您发布的代码相同的代码 "formatted":
ICommand command = (ICommand)o.ToObject(commandType);
RequestStatus result = _commandDispatcher.Dispatch<ICommand>(command);
所以您不是调用 ICommandDispatcher.Dispatch<commandType>(command)
,而是使用类型参数 ICommand
调用它。您需要使用反射 select 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;
}
我正在尝试将 Azure 消息队列与类似于 CQRS 中使用的命令模式一起使用。
这是一个示例命令:
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,那么它会按预期工作。这显然是不可接受的,我认为如果我有一个对象和一个完全限定的 class 名称,那么转换起来不会太困难。
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);
}
让我编写与您发布的代码相同的代码 "formatted":
ICommand command = (ICommand)o.ToObject(commandType);
RequestStatus result = _commandDispatcher.Dispatch<ICommand>(command);
所以您不是调用 ICommandDispatcher.Dispatch<commandType>(command)
,而是使用类型参数 ICommand
调用它。您需要使用反射 select 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;
}