Simple Injector注册并执行内部CommandHandler

时间:2016-07-07 12:26:23

标签: c# .net simple-injector

在我的开发团队中,我们正在使用this article中所述的CQRS模式,并使用推荐 DI容器:Simple Injector双关语)。

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

  • 01 WebApp references => [02] [03]
  • 02合同
  • 03 BusinessLayer

Project 01是客户端(ASP.NET MVC 4应用程序),我们使用Simple Injector(Bootstrapper)注册我们的应用程序服务。 Project 03是文章中描述的所有Command和Query处理程序的定义。 Project 02是定义命令和查询的地方。为了使Simple Injector注册处理程序,客户端可以直接引用业务层程序集。

容器注册这样的命令句柄

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

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

public class AddCustomerCommandHandler : ICommandHandler<AddCustomerCommand> { ... }
他写道:

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

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

  

直接在命名空间内声明的类和结构(in   换句话说,没有嵌套在其他类或结构中)可以   是公共的还是内部的。 如果没有访问权限,则内部是默认设置   指定了修饰符。

现在内部定义为:

  

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

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

1 个答案:

答案 0 :(得分:2)

在以前的版本中,Simple Injector的批量注册默认跳过内部类型,并且还允许您注册内部类型。

此方法已证明存在问题,因为在某些应用程序中,缺少的类型允许应用程序继续运行,同时显示不正确的行为(因此无声地失败)。

为了防止这种情况,我们改变了这种行为,现在您总是会看到这种类型的注册。这个想法是,默默地跳过预期的类型比其他任何东西都要糟糕得多。由于大多数应用程序都是完全信任的,因此可以构建和解析内部类型,因此从Simple Injector的角度来看,类型是内部的很好。对于其他应用程序类型,对Verify的调用将快速检测到不可构造的类型。

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

在您的情况下失败的原因很可能是因为您派遣处理程序并在调度期间使用dynamic键入。您所看到的是IMO在C#动态基础架构中的一个怪癖。 C#尝试使用反射找到您的Handle方法,但Handle方法是内部的,因为它的周围类型是内部的。即使类型实现包含该方法的公共接口,它也无法找到此方法。这是一个怪癖,我相信C#应该仍然会找到这种方法;但它并没有。这就是您的代码失败的原因。

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

您还可以在Simple Injector中集成检查,您可以有条件地注册最外面的装饰器,并且如果处理程序是内部的,则让谓词抛出异常。