【发布时间】:2017-07-13 04:17:40
【问题描述】:
我正在基于现有的 Asp.Net 4.5 解决方案创建一个新的 Asp.Net Core 解决方案。
当前的解决方案使用 Microsoft Unity 容器,并且基础结构引用了服务定位器。
我想摆脱服务定位器并避免在我的新基础架构中引用特定的 DI 容器。
我想出一个好方法来替换当前的命令/查询/事件调度程序,而不需要任何 DI 容器依赖项。
这是我的 Dispatcher 类
public class Dispatcher : IDispatcher
{
private const string HandleMethodName = "Handle";
public TResponse Request<TResponse>(IQuery<TResponse> query)
{
Type queryType = query.GetType();
// used for when OperationResult<object> was used
Type operationResultTrueReturnType = typeof(TResponse);
if (operationResultTrueReturnType == typeof(object))
{
operationResultTrueReturnType = queryType.GetInterface(typeof(IQuery<>).Name).GenericTypeArguments[0];
}
Type handlerType = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), operationResultTrueReturnType);
return ExecuteHandler<TResponse>(handlerType, query, queryType);
}
public OperationResult Submit(ICommand command)
{
Type commandType = command.GetType();
var baseTypeAttribute = (CommandBaseTypeAttribute)commandType.GetCustomAttributes(typeof(CommandBaseTypeAttribute), false).FirstOrDefault();
if (baseTypeAttribute != null)
commandType = baseTypeAttribute.BaseType;
try
{
Type handlerType = typeof(ICommandHandler<>).MakeGenericType(commandType);
return ExecuteHandler<OperationResult>(handlerType, command, commandType);
}
catch (InvalidOperationException ex)
{
return new OperationResult(OperationResultStatus.Failure, ex.Message);
}
}
public OperationResult<TResult> Submit<TResult>(ICommand<TResult> command)
{
Type commandType = command.GetType();
var baseTypeAttribute = (CommandBaseTypeAttribute)commandType.GetCustomAttributes(typeof(CommandBaseTypeAttribute), false).FirstOrDefault();
if (baseTypeAttribute != null)
commandType = baseTypeAttribute.BaseType;
try
{
Type handlerType = typeof(ICommandHandler<,>).MakeGenericType(commandType, typeof(TResult));
return ExecuteHandler<OperationResult<TResult>>(handlerType, command, commandType);
}
catch (InvalidOperationException ex)
{
return new OperationResult<TResult>(OperationResultStatus.Failure, default(TResult), ex.Message);
}
}
public void Raise(IDomainEvent domainEvent)
{
Type domainEventType = domainEvent.GetType();
try
{
Type handlerType = typeof(ICommandHandler<>).MakeGenericType(domainEventType);
ExecuteHandler(handlerType, domainEvent, domainEventType);
}
catch (InvalidOperationException)
{
}
}
private static void ExecuteHandler(Type handlerType, object argument, Type argumentType)
{
object handler = ServiceLocator.Current.GetInstance(handlerType);
if (handler == null)
throw new ConfigurationErrorsException("Handler not registered for type " + argumentType.Name);
try
{
MethodInfo handleMethod = handlerType.GetMethod(HandleMethodName, new[] { argumentType });
handleMethod.Invoke(handler, new[] { argument });
}
catch (TargetInvocationException ex)
{
if (ex.InnerException != null)
throw ex.InnerException;
throw;
}
}
private static TReturnValue ExecuteHandler<TReturnValue>(Type handlerType, object argument, Type argumentType)
{
object handler = ServiceLocator.Current.GetInstance(handlerType);
if (handler == null)
throw new ConfigurationErrorsException("Handler not registered for type " + argumentType.Name);
try
{
MethodInfo handleMethod = handlerType.GetMethod(HandleMethodName, new[] { argumentType });
return (TReturnValue)handleMethod.Invoke(handler, new[] { argument });
}
catch (TargetInvocationException ex)
{
if (ex.InnerException != null)
throw ex.InnerException;
throw;
}
}
}
ExecuteHandler 有 ServiceLocator 调用。
不使用如何处理?
【问题讨论】:
-
解决方案是制作一个 infrastructure 组件,它是您注入容器的组合根目录的一部分(替换此 Dispatcher)。有关示例,请参见 the QueryProcessor class here。
-
我喜欢 NightOwl888 的建议。如果您想抽象出服务定位器,则让调度程序显式依赖将用于执行解析的服务提供者 (IServiceProvider)。
-
由于处理程序基本上是单例,你应该能够在你的处理程序上添加约束,即
ICommand作为命令的标记接口,然后命令处理程序将是ICommandHandler<ICommand>。将您的处理程序注册为单例,然后通过IEnumerable<ICommandHandler<ICommand>>解析并将它们注册到IDictionary<Type, ICommandHandler<ICommand>>以便于访问。这样,您还可以避免直接使用容器并将调度程序从“应用程序层”移动到核心层之一(服务/域层)
标签: c# asp.net asp.net-core cqrs