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

2024-02-07

我一直在使用与所描述的非常相似的模式在这篇优秀的文章中 http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91将命令和查询作为对象。我还使用 SimpleInjector 作为 DI 容器。

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

So my Dispatcher对象构造函数如下所示:

public CommandAndQueryDispatcher(IEnumerable<ICommandHandler> commandHandlers,
    IEnumerable<IQueryHandler> queryHandlers)
{
}

我的 CommandHandler 接口如下所示:

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 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...
    }
}

然而,这种变化有一些影响,现在我想在我的命令和查询中添加一些装饰器,但我遇到了一些问题。

为了将所有命令和查询注入到Dispatcher我让它们都有一个基础的、非通用的界面ICommandHandler and IQueryHandler,然后询问实际接收到的实例(这是通用的)以获取它们处理的命令类型以注册它们,以便我稍后可以根据给定命令的类型查找处理程序。

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

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

所以我的问题是

  • 如何从容器中获取开放泛型类型的所有实例作为基本接口传递到我的Dispatcher?

OR

  • 有没有更好的方法来实现调度程序功能,以便我的控制器可以不知道哪个处理程序将处理与 SimpleInjector 配合得更好的命令/查询?

这将减少构造函数所需的参数数量 使整个东西更容易使用

请密切关注这一点,因为这样做可能会掩盖您的控制器做得太多的事实;违反单一责任原则 https://en.wikipedia.org/wiki/Single_responsibility_principle。违反 SRP 往往会导致以后出现可维护性问题。甚至还有一个后续文章 https://cuttingedge.it/blogs/steven/pivot/entry.php?id=92您提到的文章的作者(顺便说一句,就是我)指出:

我当然不会提倡 ICommandProcessor [那是 ICommandAndQueryDispatcher 在你的情况下]用于执行命令 - 消费者不太可能依赖许多命令 处理程序,如果他们这样做可能会违反 SRP。 (source https://cuttingedge.it/blogs/steven/pivot/entry.php?id=92)

本文甚至讨论了查询的解决方案,但您也可以将其应用到您的命令中。但您应该考虑剥离您的解决方案并删除非通用接口。你不需要它们。

相反,定义以下内容:

public interface ICommandHandler<TCommand> : where TCommand : ICommand
{
    void Execute(TCommand command);
}

请注意以下几点:

  1. 没有非通用接口。
  2. 你没有通过ICommandAndQueryDispatcher。那太丑了。这ICommandAndQueryDispatcher是一个服务,并且服务需要通过构造函数注入来传递。这command另一方面是运行时数据,并且运行时数据使用方法参数传递 https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=99。因此,如果有一个命令或查询处理程序需要调度程序:通过构造函数注入它。
  3. 没有in关键字为TCommand。由于命令是用例,因此应该命令和命令处理程序实现之间的一对一映射。然而,指定“in”意味着一个命令类可以映射到多个处理程序,但情况不应如此。另一方面,当处理事件和事件处理程序时,这将是一种更明显的方法。
  4. No more CommandHandlerBase<TCommand>。你不需要那个。我认为好的设计几乎不需要基类。

另一件事是,不要尝试将命令调度程序与查询调度程序混合在一起。两种责任意味着两种阶级。这是您的命令调度程序的外观:

// This interface is defined in a core application layer
public interface ICommandDispatcher
{
    void Execute(ICommand command);
}

// This class is defined in your Composition Root (where you wire your container)
// It needs a dependency to the container.
sealed class CommandDispatcher : ICommandDispatcher
{
    private readonly Container container;

    public CommandDispatcher(Container container) {
        this.container = container;
    }

    public void Execute(ICommand command) {
        var handlerType = typeof(ICommandHandler<>)
            .MakeGenericType(command.GetType());

        dynamic handler = container.GetInstance(handlerType);

        handler.Handle((dynamic)command);
    }
}

请注意,您不会在此处注入命令处理程序的集合,而是从容器请求处理程序。该代码仅包含基础设施,没有业务逻辑,因此,如果您将此实现靠近负责连接容器的代码,您将not http://blog.ploeh.dk/2011/08/25/ServiceLocatorrolesvs.mechanics/滥用服务定位器反模式 http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/,这是一个有效的方法。在这种情况下,应用程序的其余部分仍然不依赖于 DI 框架。

你可以注册这个CommandDispatcher如下:

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为泛型接口配置装饰器,并在简单注入器中将所有实例注入到具有非泛型接口参数的构造函数 的相关文章

随机推荐

  • c# - 如何以编程方式检查解决方案中的引用是否配置正确

    我们遇到了一些构建错误 需要手动修复引用 我想以编程方式检查是否定义了项目引用而不是 dll 引用 因此 请检查解决方案中的所有引用 并针对每个项目检查引用的定义是否正确 这样相关的依赖就会被正确配置 有人知道现有的解决方案吗 我想要一个在
  • 如何将WPF动画逐帧渲染为视频?

    我在 WPF 中创建了一个我喜欢的很好的效果 我希望能够 记录 这个效果 但运行时却很不稳定 有没有办法可以将其转换为逐帧过程并自己将其另存为视频 而不是尝试在播放期间录制它 基本上我想将 WPF 渲染为视频 或列出一系列保存为位图的帧 关
  • Nest.js 中使用 @nestjs/passport 进行可选身份验证

    我有一条需要经过身份验证和未经身份验证的用户使用的路线 我用 UseGuards AuthGuard jwt 启用身份验证 但它会阻止任何未经身份验证的用户访问该路由 正常 如何允许未经身份验证的用户也访问该路由 似乎没有我可以传递的选项A
  • 如何在 Git 中分离子目录但保留所有分支

    我一直在关注这个问题的答案将子目录分离到单独的 Git 存储库中 https stackoverflow com questions 359424 detach subdirectory into separate git reposito
  • UITableView 不重用单元格,这样做的好例子吗?

    我的 UITableView 有 5 个单元格 每个都有一个 UITextField 作为子视图 用户将在其中输入数据 如果我确实使用单元格重用 则当单元格滚动到视图之外时 文本字段将被清除 我不想处理这个问题 有没有办法不重复使用单元格
  • 如何编辑引导程序导航栏的样式?

    我正在尝试编辑引导导航栏的样式 但是 例如 我无法编辑颜色和删除边框 我想将颜色更改为白色 并将边框颜色更改为白色 并且我已包含以下代码 谁能告诉我我做错了什么 navbar default background color ffffff
  • 更改单元格中某些字符的颜色

    我在单元格 A1 中有一句话 我想要其中 50 个 我想将任何数字字符设置为红色文本 只是数字字符 我该怎么做呢 这是我所拥有的框架 Sub RedText Dim i As Integer For i 1 To Len Cells 1 1
  • webpack.config.js 不断将bundle.js 的脚本标记添加到我的index.html 中

    我有如下的 webpack config js var path require path var webpack require webpack var HtmlWebpackPlugin require html webpack plu
  • Angular,如何从字符串解析模板并传递当前上下文变量

    我制作了一个简单的组件来创建表 Component selector admin table template table class table table bordered thead th column label th thead
  • 如何在 if 语句中使用 grep?

    我有以下命令可以给出正确的结果 grep include java Ri System loadLibrary 但是 如果我将它放在 if 条件中 无论字符串是否存在 它总是返回相同的 0 结果 if grep include java R
  • 如何取消引用已传递给子例程的 Perl 哈希引用?

    我仍在尝试解决我的哈希取消引用问题 我当前的问题是我现在将 hashref 传递给子组件 并且我想在该子组件中取消引用它 但我没有找到正确的方法 语法来做到这一点 在 sub 中 我想迭代哈希键 但 hashref 的语法与哈希不同 我知道
  • Java:需要从字节数组创建 PDF

    我从 DB2 表中得到了 blob 我正在将其转换为字节数组 以便我可以使用它 我需要获取字节数组并创建一个PDF出来了 这就是我所拥有的 static void byteArrayToFile byte bArray try Create
  • 哪些列表可以作为临时列表?

    当处理项目列表时 列表仅充当暂时的容器 您建议我使用哪些列表类型 I 不想手动销毁列表 想要使用built in列表类型 无框架 库 想要仿制药 可以在不导致泄漏的情况下实现这一点的东西 function GetListWithItems
  • 使用ionic安装和卸载cordova应用程序时执行脚本

    我使用 cordova 已经有好几年了 使用 ionic 的时间还不到一年 我正在寻找在安装应用程序和卸载应用程序时运行 JavaScript 函数的方法 我做了很多搜索 但没有找到任何相关内容 有人有一个想法 至少有一个近似值可以作为起点
  • 让menhir将用户定义的函数从.mly添加到.mli

    Menhir 允许将任意 ocaml 代码添加到 mly 文件的末尾 我想在其中声明一些函数 但我找不到一种方法让 menhir 将我的函数添加到 mli 文件中 以便它们从其他模块中可见 是否可以 答案很简单 那就是no 中定义的代码 m
  • 解决导航属性 Dynamics WebAPI 深度插入时出现的错误

    我正在使用微软动态Web API https msdn microsoft com en us library mt607689 aspx将数据写入 Microsoft Dynamics 365 中的实体 当我尝试执行深插入 https m
  • 用于 Angular.js 依赖注入管理的 Grunt.js

    我正在使用 Grunt 自动构建我的网络应用程序 我遇到了一个有趣的问题 我有两个选择 1 grunt dev and 2 grunt build grunt dev只执行与开发相关的基本任务 我的 主要 Angular 模块如下所示 va
  • Py_None 的值

    我很清楚None用于表示缺乏价值 但由于在实现过程中一切都必须有一个潜在的价值 所以我想看看使用了什么值来表示没有值 关于CPython 我理解 基于文档 https docs python org 3 c api none html c
  • 无法使用 $http angularjs 获取结果数据

    我尝试使用 http 但为什么它返回 null 结果 angular module myApp factory sender function http var newData null http get test html success
  • 为泛型接口配置装饰器,并在简单注入器中将所有实例注入到具有非泛型接口参数的构造函数

    我一直在使用与所描述的非常相似的模式在这篇优秀的文章中 http www cuttingedge it blogs steven pivot entry php id 91将命令和查询作为对象 我还使用 SimpleInjector 作为