简单的注入器注册和执行内部命令处理程序

本文关键字:执行 内部 命令处理程序 注册 注入器 简单 | 更新日期: 2023-09-27 17:55:32

在我的开发团队中,我们使用本文中所述的 CQRS 模式,并使用推荐的 DI 容器:简单注入器(双语)。

这是我们当前的项目结构:

  • 01 网络应用references => [02] [03]
  • 02 合同
  • 03 业务层

项目 01 是客户端(ASP.NET MVC 4 应用程序),我们在其中使用 Simple Injector ( Bootstrapper ) 注册应用程序的服务。项目 03 是定义所有命令和查询处理程序的位置,如本文中所述。项目 02 是定义命令和查询的地方。为了使简单注入器注册处理程序,客户端具有对业务层程序集的直接引用。

容器像这样注册命令处理程序

container.Register(typeof(ICommandHandler<>), assemblies);

现在的问题是,我们的一位开发人员不小心忘记将他的一个处理程序类声明为 public 。所以代替:

public class AddCustomerCommandHandler : ICommandHandler<AddCustomerCommand> { ... }

他写道:

class AddCustomerCommandHandler : ICommandHandler<AddCustomerCommand> { ... }

现在根据 msdn,如果省略访问修饰符,则使用内部:

直接在命名空间中声明的类和结构(在 其他单词,未嵌套在其他类或结构中)可以 可以是公共的,也可以是内部的。如果没有访问权限,则默认为内部 指定修饰符。

现在内部定义为:

类型或成员可由同一程序集中的任何代码访问, 但不是来自另一个程序集。

尝试执行此命令后,运行时不会引发任何错误,并且命令会愉快地执行。我对此感到惊讶,因为我预计简单注射器会出现验证错误,但事实并非如此。此外,当尝试从客户端内直接实例化处理程序对象时,编译器给了我一个错误,it cannot access an internal class here,正如预期的那样!那么为什么简单注入器能够在它的访问修饰符internal时注册这个命令处理程序呢?

简单的注入器注册和执行内部命令处理程序

在以前的版本中,Simple Jector 的批处理注册默认跳过内部类型,并且一个标志也允许您注册内部类型。

此方法已被证明是有问题的,因为在某些应用程序中,缺少的类型允许应用程序继续运行,同时显示不正确的行为(因此以静默方式失败)。

为了防止这种情况,我们更改了此行为,现在你将始终看到此类类型也已注册。这个想法是,静默跳过预期的类型比其他任何事情都糟糕得多。由于大多数应用程序在完全信任环境中运行,因此可以构造和解析内部类型,因此从Simple Injector的角度来看,类型可以是内部类型。对于其他应用程序类型,对 Verify 的调用将快速检测不可构造的类型。

同样从应用程序的角度来看,类型是内部的应该没有问题,因为使用者与这种类型实现的公共接口通信。

在您的情况下,此操作失败的原因很可能是因为您在调度过程中调度处理程序并使用dynamic键入。您所看到的是 IMO 是 C# 动态基础结构中的一个怪癖。C# 尝试使用反射查找Handle方法,但 Handle 方法是内部方法,因为它的周围类型是内部类型。它找不到此方法,即使该类型实现了包含该方法的公共接口。这是一个怪癖,我相信 C# 仍然应该找到该方法;但事实并非如此。这就是你的代码失败的原因。

您可以做几件事来防止将来出现此类问题。例如,您可以定义一个单元测试来检查是否所有处理程序都是公共的。或者,您可以使用处理程序作为构造函数参数定义一个特殊的泛型和公共包装类,并解析该包装器而不是处理程序。然后,您可以在该包装器上使用动态类型。或者您注册了一个最外层的装饰器,以确保它是公开的。C# 反映最外层的类型,因此这将起作用。

您还可以在简单注入器中集成签入,其中有条件地注册最外层的装饰器,并在处理程序是内部处理程序时让谓词引发异常。