IoC 容器,在编译时检查错误 [英] IoC container, check for errors at compile time

查看:32
本文介绍了IoC 容器,在编译时检查错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的问题.

假设我有一个 .Net 解决方案,有不同的项目,比如一些类库(bll、dal 等)和一个可以是 web 应用程序或 wpf 应用程序的主项目,没关系.

Let's say I have a .Net solution, with different projects like some class libraries (bll, dal, etc) and a main project which can be a web application or a wpf application, it doesn't matter.

现在假设我想使用 IoC 容器(如 Windsor、Ninject、Unity 等)来解析验证器、存储库、通用接口实现等内容.

Now let's say I want to use an IoC container (like Windsor, Ninject, Unity, etc) to resolve stuff like validators, repositories, common interface implementations and such.

我把它们放在一起.编译并运行良好.然后,有一天,我添加了一个新服务,在我的代码中,我只是尝试通过 IoC 容器来解决它.问题是,我忘记在 IoC 配置中注册它.

I put it all together. Compiles and runs fine. Then, someday, I add a new service, and in my code I just try to resolve it through the IoC container. Thing is, I forget to register it in the IoC configuration.

一切都经过编译,应用程序得到部署和运行.一切正常,除非页面的代码向容器请求该新服务,而容器回答嘿,我对这项服务一无所知".

Everything compiles, and the application gets deployed and runs. All works fine, except for when a page's code asks for that new service to the container, and the container answers "hey, I don't know anything about this service".

您会记录您的错误,以及用户友好的错误页面.你会去检查错误,看到问题并修复它.很标准.

You get your error logged, and the user friendly error page. You'll go check the error, see the problem and fix it. Pretty standard.

现在假设我们想要改进流程,并以某种方式能够在编译时知道我们期望 IoC 容器处理的每个服务是否在代码中正确注册.

Now let's say we want to improve the process, and in some way be able to know at compile time if every service that we expect the IoC container to handle is registered correctly in the code.

这是怎么实现的?有一件事,单元测​​试被排除在可能的答案之外,我正在寻找另一种方法,如果它确实存在的话.

How could this be achieved? One thing, Unit Tests are ruled out from possible answers, I'm looking for another way, if it does exist.

想法?

编辑 - 经过一些回答和评论后,似乎单元测试确实是实现此功能的唯一方法.

EDIT - After some answers and comments, it seems that Unit Tests are indeed the only way to achieve this feature.

我想知道的是,如果单元测试 - 由于任何原因 - 不可能,因此无法在编译时测试 IoC,这是否会阻止您使用 IoC 容器并选择直接实例化所有在你的代码?我的意思是,你会不会认为使用 IoC 和后期绑定太不安全和风险太大,并且看到它的优势被这个缺陷"所超越?

What I'd like to know is, if Unit Tests were - for any reason - not possible, and thus IoC could not be tested at compiled time, would this prevent you from using an IoC container and opting for direct instantiation all over your code? I mean, would you consider too unsafe and risky to use IoC and late binding, and see its advantages being outscored by this "flaw"?

推荐答案

编译器不可能验证整个程序的工作情况.您的程序编译的事实并不意味着它可以正常工作(即使不使用 IoC).为此,您将需要自动化测试和手动测试.这并不意味着您不应该尝试让编译器尽可能多地做,但是因为这个原因远离 IoC 是不好的,因为 IoC 旨在保持您的应用程序的灵活性、可测试性和可维护性.没有 IoC,您将无法正确测试您的代码,而没有任何自动化测试,几乎不可能编写任何合理规模的可维护软件.

It is impossible for the compiler to validate the working of your whole program. The fact that your program compiles, doesn't mean it works correctly (even without using IoC). For that you'll need both automated tests and manual testing. This doesn't mean that you shouldn't try to let the compiler do as much as it can, but staying away from IoC for that reason is bad, since IoC is meant to keep your application flexible, testable and maintainable. Without IoC you won't be able to test your code properly, and without any automated tests it is almost impossible to write any reasonably sized maintainable software.

然而,拥有 IoC 提供的灵活性,确实意味着某些特定代码段所具有的依赖关系无法再由编译器进行验证.所以你需要用另一种方式来做到这一点.

Having the flexibility as IoC provides however, does mean that the dependencies some particular piece of code has, can't be validated anymore by the compiler. So you need to do this in another way.

某些 DI 框架允许您验证容器的正确性.例如,Simple Injector 包含一个 Verify() 方法,该方法将简单地遍历所有注册并为每个注册解析一个实例.通过在应用程序启动期间调用此方法(或使用类似方法),您将在(开发人员)测试期间发现 DI 配置是否有问题,这将阻止应用程序启动.您甚至可以在单元测试中执行此操作.

Some DI frameworks allow you to verify the container for correctness. Simple Injector for instance, contains a Verify() method, that will simply iterate over all registrations and resolve an instance for each registration. By calling this method (or using a similar approach) during application startup, you will find out during (developer) testing if something is wrong with the DI configuration and it will prevent the application from starting. You could even do this in a unit test.

但重要的是,测试 DI 配置不需要太多维护.如果你必须为你注册的每种类型添加一个单元测试来验证容器,你会失败,因为缺少注册(因此缺少单元测试)将是首先失败的原因.

Important however is, that testing the DI configuration should not need much maintenance. If you must add a unit test for each type that you register to verify the container, you will fail, simply because the missing registration (and thus a missing unit test) will be the reason to fail in the first place.

这为您提供了几乎"编译时支持.但是,您需要注意应用程序的设计以及将事物连接在一起的方式.以下是一些提示:

This gives you 'almost' compile-time support. However, you need to be conscious about the design of your application and the way you wire things together. Here are some tips:

  1. 远离隐式属性注入,如果容器找不到已注册的依赖项,则允许容器跳过属性注入.这将不允许您的应用程序快速失败,并且稍后将导致 NullReferenceExceptions.显式属性注入,您可以强制容器注入属性,但是,请尽可能使用构造函数注入.
  2. 如果可能,明确注册所有根对象.例如,在容器中显式注册所有 ASP.NET MVC Controller 实例.通过这种方式,容器可以从根对象开始检查完整的依赖关系图.您应该以自动方式注册所有根对象,例如通过使用反射来查找所有根类型.例如,Simple Injector 的 MVC3 集成 NuGet 包 包含一个 RegisterMvcControllers 将为您执行此操作的扩展方法.其他容器的集成包包含类似的功能.
  3. 如果注册根对象不可能或不可行,请在启动期间手动测试每个根对象的创建.例如,对于 ASP.NET Web 窗体 Page 类,您可能会从它们的构造函数中调用容器(因为很遗憾 Page 类必须具有默认构造函数).这里的关键再次是使用反射一次找到它们.通过使用反射查找所有 Page 类并实例化它们,您将发现(在应用启动或测试期间)您的 DI 配置是否存在问题.
  4. 让您的 IoC 容器为您管理的所有服务都有一个公共构造函数.多个构造函数会导致歧义,并可能以不可预测的方式破坏您的应用程序.拥有多个构造函数是一种反模式.
  5. 在某些情况下,在应用程序启动期间还无法创建某些依赖项.为确保应用程序可以正常启动并且仍然可以验证其余的 DI 配置,请在代理或抽象工厂后面抽象这些依赖项.
  1. Stay away from implicit property injection, where the container is allowed to skip injecting the property if it can't find a registered dependency. This will disallow your application to fail fast and will result in NullReferenceExceptions later on. Explicit property injection, where you force the container to inject a property is fine, however, use constructor injection whenever possible.
  2. Register all root objects explicitly if possible. For instance, register all ASP.NET MVC Controller instances explicitly in the container. This way the container can check the complete dependency graph starting from the root objects. You should register all root objects in an automated fashion, for instance by using reflection to find all root types. The MVC3 Integration NuGet Package of the Simple Injector for instance, contains a RegisterMvcControllers extension method that will do this for you. Integration packages of other containers contain similar features.
  3. If registering root objects is not possible or feasible, test the creation of each root object manually during startup. With ASP.NET Web Form Page classes for instance, you will probably call the container from within their constructor (since Page classes must unfortunately have a default constructor). The key here again is finding them all in once using reflection. By finding all Page classes using reflection and instantiating them, you'll find out (during app start-up or test time) whether there is a problem with your DI configuration or not.
  4. Let all services that your IoC container manages for you have a single public constructor. Multiple constructors result in ambiguity and can break your application in unpredictable ways. Having multiple constructors is an anti-pattern.
  5. There are scenarios where some dependencies can not yet be created during application start-up. To ensure that the application can be started normally and the rest of the DI configuration can still be validated, abstract those dependencies behind a proxy or abstract factory.

这篇关于IoC 容器,在编译时检查错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆