简体   繁体   English

没有服务定位器的CQRS调度程序

[英]CQRS Dispatcher Without Service Locator

I am creating a new Asp.Net Core solution based on an existing Asp.Net 4.5 solution. 我正在基于现有的Asp.Net 4.5解决方案创建一个新的Asp.Net Core解决方案。

The current solution uses Microsoft Unity Container and the Infrastructure has references to the Service Locator. 当前的解决方案使用Microsoft Unity Container,而Infrastructure则引用了Service Locator。

I want to get rid of the Service Locator and avoid referencing specific DI containers in my new Infrastructure. 我想摆脱服务定位器,避免在我的新基础设施中引用特定的DI容器。

I'm having an issue coming up with a good way to replace the current Command/Query/Event Dispatcher without any DI container dependencies. 我遇到了一个问题,即在没有任何DI容器依赖性的情况下替换当前的Command / Query / Event Dispatcher的好方法。

Here is my Dispatcher class 这是我的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 has the ServiceLocator call. ExecuteHandler具有ServiceLocator调用。

How can I handle this without using it? 如何在不使用它的情况下处理这个问题?

I like the suggestion provided in the comments. 我喜欢评论中提供的建议。 If you want to abstract away the service locator then have the dispatcher explicitly depend on an abstract service provider (like IServiceProvider ) that will be used to do resolutions. 如果要抽象出服务定位器,则让调度程序显式依赖于将用于执行解析的抽象服务提供程序(如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;
        }
    }    
}

The dispatcher is now no longer tightly coupled to the service locator anti-pattern and allows for any derived provider to be used. 调度程序现在不再与服务定位器反模式紧密耦合,并允许使用任何派生的提供程序。 This allows you to avoid referencing specific DI containers. 这样可以避免引用特定的DI容器。

I added an IServiceProvider to the Dispatcher's constructor. 我在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;
        }
    }
}

Then, injected it in Startup on the client. 然后,在客户端的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