C# 为泛型接口配置装饰器,并在简单注入器中使用非泛型接口参数将所有实例注入构造函数

C# 为泛型接口配置装饰器,并在简单注入器中使用非泛型接口参数将所有实例注入构造函数,c#,generics,dependency-injection,decorator,simple-injector,C#,Generics,Dependency Injection,Decorator,Simple Injector,我一直在使用一种模式,非常类似于所描述的将命令和查询作为对象的模式。我还将SimpleInjector用作DI容器 唯一显著的区别是控制器对某个ICommandHandler具有显式依赖性,我希望控制器对对象(一个Dispatcher)具有依赖性,该对象将获取一个ICommand实例,并为该命令解析正确的处理程序。这将减少构造函数需要获取的参数数量,并使整个过程更易于使用 因此,我的Dispatcherobjects构造函数如下所示: public CommandAndQueryDispatch

我一直在使用一种模式,非常类似于所描述的将命令和查询作为对象的模式。我还将SimpleInjector用作DI容器

唯一显著的区别是控制器对某个
ICommandHandler
具有显式依赖性,我希望控制器对对象(一个
Dispatcher
)具有依赖性,该对象将获取一个
ICommand
实例,并为该命令解析正确的处理程序。这将减少构造函数需要获取的参数数量,并使整个过程更易于使用

因此,我的
Dispatcher
objects构造函数如下所示:

public CommandAndQueryDispatcher(IEnumerable<ICommandHandler> commandHandlers,
    IEnumerable<IQueryHandler> queryHandlers)
{
}
public interface ICommandHandler<in TCommand> : ICommandHandler 
    where TCommand : ICommand
{
    void Execute(TCommand command, ICommandAndQueryDispatcher dispatcher);
}

public interface ICommandHandler
{
    void Execute(object command, ICommandAndQueryDispatcher dispatcher);
}
public命令和querydispatcher(IEnumerable commandHandler,
IEnumerable查询句柄)
{
}
我的CommandHandler接口如下所示:

public CommandAndQueryDispatcher(IEnumerable<ICommandHandler> commandHandlers,
    IEnumerable<IQueryHandler> queryHandlers)
{
}
public interface ICommandHandler<in TCommand> : ICommandHandler 
    where TCommand : ICommand
{
    void Execute(TCommand command, ICommandAndQueryDispatcher dispatcher);
}

public interface ICommandHandler
{
    void Execute(object command, ICommandAndQueryDispatcher dispatcher);
}
公共接口ICommandHandler:ICommandHandler
其中TCommand:ICommand
{
void Execute(TCommand命令、icommandadquerydispatcher调度器);
}
公共接口ICommandHandler
{
void Execute(对象命令,icommandadquerydispatcher调度器);
}
典型的命令处理程序如下所示:

public abstract class CommandHandlerBase<TCommand> : ICommandHandler<TCommand> 
    where TCommand : ICommand
{
    public abstract void Execute(TCommand command, ICommandAndQueryDispatcher dispatcher);

    public void Execute(object command, ICommandAndQueryDispatcher dispatcher)
    {
        Execute((TCommand) command, dispatcher);
    }
}

internal class DeleteTeamCommandHandler : CommandHandlerBase<DeleteTeamCommand>
{
    public DeleteTeamCommandHandler(){        }

    public override void Execute(DeleteTeamCommand command, 
        ICommandAndQueryDispatcher dispatcher)
    {
       ... functionality here...
    }
}
公共抽象类CommandHandlerBase:ICommandHandler
其中TCommand:ICommand
{
公共抽象void Execute(TCommand命令、icommandadquerydispatcher调度程序);
public void Execute(对象命令,icommandadquerydispatcher调度程序)
{
执行((TCommand)命令,调度程序);
}
}
内部类DeleteTeamCommandHandler:CommandHandlerBase
{
公共DeleteTeamCommandHandler(){}
公共覆盖无效执行(DeleteTeamCommand命令,
ICommandadQueryDispatcher(调度员)
{
…功能在这里。。。
}
}
然而,这个改变有一些缺陷,现在我想给我的命令和查询添加一些装饰程序,我遇到了一些问题

为了将所有的命令和查询注入到
调度程序中
,我让它们都有一个基本的、通用的接口
ICommandHandler
IQueryHandler
,然后询问实际接收到的实例(这是通用的)要获取它们处理的命令类型,请注册它们,以便稍后根据给定命令的类型查找处理程序

现在,当我尝试使用示例中所示的装饰器时,我似乎无法将任何内容注入到我的
Dispatcher
,因为装饰的实例注册为泛型类型,所以不能解析为基本
ICommandHandler
实例。如果我尝试使decorators非泛型,那么注入的实例没有任何泛型类型参数,因此我无法找到它作为处理程序的命令类型

我觉得我一定错过了一些相当简单的东西

所以我的问题是

  • 如何从作为基本接口的容器强制转换中获取开放泛型类型的所有实例,并将其传递到我的
    Dispatcher

  • 对于我来说,有没有更好的方法来实现dispatcher功能,这样我的控制器就可以不知道哪个处理程序将处理与SimpleInjector配合更好的命令/查询
这将减少构造函数需要的参数数量 让整个东西更容易使用

请密切注意这一点,因为这样做,你可能会掩盖你的控制器做得太多的事实;违反法律。SRP违规往往会导致以后的可维护性问题。你提到的那篇文章的作者(顺便说一句)甚至有一段话说:

我当然不会提倡ICommandProcessor[那是 ICommandadQueryDispatcher]用于执行命令- 消费者不太可能依赖许多命令 处理程序,如果他们这样做,可能会违反SRP。()

本文甚至讨论了一种查询解决方案,但您也可以将其应用于命令。但是你应该考虑剥离你的解决方案并移除非泛型接口。你不需要它们

相反,请定义以下内容:

public interface ICommandHandler<TCommand> : where TCommand : ICommand
{
    void Execute(TCommand command);
}
请注意,此处不注入命令处理程序集合,而是从容器请求处理程序。此代码将只包含基础结构,而不包含业务逻辑,因此如果将此实现放在负责连接容器的代码附近,则会滥用,这是一种有效的方法。在这种情况下,应用程序的其余部分仍然不依赖于DI框架

您可以按如下方式注册此
CommandDispatcher

container.RegisterSingle<ICommandDispatcher>(new CommandDispatcher(container));
container.RegisterSingle(新的CommandDispatcher(container));

如果采用这种方法,因为您通过
ICommandHandler
接口请求处理程序,因此容器将自动使用必须根据您的配置和应用的泛型类型约束应用的任何修饰符包装处理程序。

谢谢。拆分命令调度程序和查询调度程序在要做的事情列表中。dispatcher通过execute方法传递,以便命令可以使用它来执行查询,以在处理命令时获取所需的数据。正是这一点触发了拆分dispatcher接口的决定,因为允许命令执行我认为没有意义的其他命令,但查询似乎没有问题。@SamHolder:请记住一条简单的规则:“通过方法调用传递运行时数据;通过构造函数注入传递[设计时]服务。”是的,我不知道为什么我没有想到通过处理程序的构造函数请求调度器。有时候你只需要从事情中退一步。Tha