[英]CQRS Dispatcher Without Service Locator
我正在基於現有的Asp.Net 4.5解決方案創建一個新的Asp.Net Core解決方案。
當前的解決方案使用Microsoft Unity Container,而Infrastructure則引用了Service Locator。
我想擺脫服務定位器,避免在我的新基礎設施中引用特定的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調用。
如何在不使用它的情況下處理這個問題?
我喜歡評論中提供的建議。 如果要抽象出服務定位器,則讓調度程序顯式依賴於將用於執行解析的抽象服務提供程序(如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容器。
我在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>();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.