簡體   English   中英

沒有服務定位器的CQRS調度程序

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM