3

写下面的代码这样正常吗:

protected void Application_BeginRequest()
{
    var container = new UnityContainer()
        .RegisterType<IUnitOfWork, MyEntities>()

    HttpContext.Current.Items["container"] = container;

    ServiceLocator.SetLocatorProvider(
        () => new UnityServiceLocator(container));
}

protected void Application_EndRequest()
{
    var container = HttpContext.Current.Items["container"] 
        as UnityContainer;

    if (container != null)
    {
        container.Dispose();
    }
}

或者为每个请求注册依赖项是一种不好的做法?

谢谢。

4

1 回答 1

5

注意:以下建议适用于所有 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 请求进行配置,并且不能由多个线程共享

于 2012-08-28T12:36:51.543 回答