注意:以下建议适用于所有 IoC 框架,并不特定于 Unity。
TLDR:不要这样做。注册一次容器Application_Start
并调用ServiceLocator.SetLocatorProvider
一次。
将依赖项注册为具有“每个请求”或“每个 Web 请求”的生命周期是很好的。然而,为每个 Web 请求创建一个新的容器实例是一种非常糟糕的做法。
容器针对解析实例(而不是注册)进行了优化,这通常意味着当第一次(从容器)请求类型时,会在后台进行一些动态代码生成。注册和代码生成和编译过程可能非常耗时,并且在每个 Web 请求上创建一个新的容器实例,将再次触发完整的注册和代码生成过程。这需要时间,生成许多临时对象(要收集更多垃圾),并生成至少在该容器实例的生命周期内必须缓存的新代码。
此外,拥有多个容器实例使得进行其他优化变得困难,例如注册范围比每个 Web 请求更大的实例。在您的情况下,将类型注册为“单例”实际上意味着“每个 Web 请求”,因为该类型在该容器实例的上下文中是单例的。或者即使不考虑性能,为了使某些类型正常工作,您需要比“每个 Web 请求”更长的生命周期(例如每个应用程序域一个实例),这更难配置。
您可能可以这样做,即使每个请求都使用一个容器,但您的代码将更难维护。
容器本身是线程安全的,因此从这个意义上说,每个请求都不需要容器实例。
在您的情况下,情况更糟,因为您的代码中有一个竞争条件,一个并发错误,因为您更改了ServiceLocator
每个请求。将ServiceLocator
注册的委托存储在私有静态字段中,这意味着它可以从所有线程访问。换句话说,您正在通过 Web 请求共享容器实例。在开发过程中您可能不会注意到,因为当时您将有一个 Web 请求,但是当您有代码调用时ServiceLocator.Current
,它会在生产中失败。以以下非常可能的场景为例,其中请求 1 启动并设置服务定位器。之后,请求 2 启动并使用它自己的容器实例覆盖服务定位器。之后,请求 1 中的代码调用ServiceLocator.Current
现在返回第二个请求的容器。有时(如果幸运的话)你会得到一个ObjectDisposedException
. 如果一个线程释放了另一个线程仍在(不正确地)使用的容器,就会发生这种情况。但是,有时会解析属于不同 Web 请求的类型(在多个线程上使用不安全的实例),这显然很糟糕,并会导致各种奇怪和不正确的行为。例如,当您返回 LINQ to SQLDataContext
或实体框架ObjectContext
实例时。这些类通常应该根据 Web 请求进行配置,并且不能由多个线程共享。