我正在基于现有的Asp.Net 4.5解决方案创建一个新的Asp.Net Core解决方案。
当前解决方案使用Microsoft Unity Container,基础结构引用了服务定位器。
我想摆脱服务定位器,避免在我的新基础架构中引用特定的DI容器。
我遇到了一个问题,即在没有任何DI容器依赖性的情况下替换当前的Command / Query / Event Dispatcher。
这是我的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调用。
如何在不使用它的情况下处理此问题?
答案 0 :(得分:2)
我喜欢评论中提供的建议。如果要抽象出服务定位器,那么让调度程序明确依赖于将用于执行解析的抽象服务提供程序(如IServiceProvider
)。
public class Dispatcher : IDispatcher {
private readonly IServiceProvider serviceProvider;
public Dispatcher (IServiceProvider serviceProvider) {
this.serviceProvider = serviceProvider;
}
//...other code removed for brevity
private object GetService(Type serviceType) {
return serviceProvider.GetService(serviceType);
}
private void ExecuteHandler(Type handlerType, object argument, Type argumentType) {
object handler = GetService(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 TReturnValue ExecuteHandler<TReturnValue>(Type handlerType, object argument, Type argumentType) {
object handler = GetService(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;
}
}
}
调度程序现在不再与服务定位器反模式紧密耦合,并允许使用任何派生的提供程序。这样可以避免引用特定的DI容器。
答案 1 :(得分:1)
我在Dispatcher的构造函数中添加了IServiceProvider
。
public class Dispatcher : IDispatcher
{
private const string HandleMethodName = "Handle";
private readonly IServiceProvider _serviceProvider;
public Dispatcher(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
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);
}
}
private TReturnValue ExecuteHandler<TReturnValue>(Type handlerType, object argument, Type argumentType)
{
object handler = _serviceProvider.GetService(handlerType);
if (handler == null)
throw new ArgumentException("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;
}
}
}
然后,在客户端的Startup中注入它。
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ResolutionDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc();
// Domain Event Handlers
services.AddTransient<IEventHandler<RequestCreatedEvent>, RequestCreatedHandler>();
// Domain Event Dispatcher
services.AddSingleton<IDomainEventDispatcher, DomainEventDispatcher>();
// Units of Work
services.AddTransient<IResolutionUnitOfWork, ResolutionUnitOfWork>();
// Commands and Queries
services.AddTransient<ICommandHandler<CreateRequestCommand, Guid>, CreateRequestHandler>();
// Command and Query Dispatcher
services.AddSingleton<IDispatcher, Dispatcher>();
}