Application_BeginRequest()方法中的依赖项注册

时间:2012-08-28 12:17:02

标签: asp.net .net dependency-injection unity-container ioc-container

编写以下代码是否正常:

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();
    }
}

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

感谢。

1 个答案:

答案 0 :(得分:5)

  

注意:以下建议适用于所有IoC框架,并非针对Unity。

TLDR :不要这样做。在Application_Start中注册一次容器,只需拨打ServiceLocator.SetLocatorProvider一次。

可以将依赖项注册为“每个请求”或“每个Web请求”生命周期。但是,每个Web请求创建一个新的容器实例是 非常糟糕的 练习。

容器被优化用于解析实例(不用于注册),这通常意味着当第一次请求类型(从容器中)时,会有一些动态代码生成。注册和代码生成和编译过程可能非常耗时,并且在每个Web请求上创建容器的新实例将重新触发完整的注册和代码生成过程。这需要时间,生成许多临时对象(要收集更多垃圾),并生成必须至少在该容器实例的生命周期内缓存的新代码。

此外,拥有多个容器实例会使其他优化变得困难,例如注册范围大于每个Web请求的实例。在您的情况下,将类型注册为“singleton”实际上意味着“每个Web请求”,因为该类型在该容器实例的上下文中是单例。或者即使没有考虑性能,要使某些类型正常工作,您需要的生命周期大于“每个网络请求”(例如每个应用程序域一个实例),这更难配置。

你可能会这样做,即使每个请求都有一个容器,但你的代码将更难维护。

容器本身是线程安全的,因此从这个意义上讲,每个请求都不需要容器实例。

在您的情况下,由于您更改了ServiceLocator,因此您的代码中存在竞争条件并发错误,这更糟糕在每个请求上。 ServiceLocator将注册的委托存储在私有静态字段中,这意味着可以从所有线程访问它。换句话说,您正在通过Web请求共享容器实例。您可能在开发期间不会注意到,因为您当时将有一个Web请求,但是当您调用ServiceLocator.Current代码时,它将在生产中失败。例如,以下非常可能的情况,其中请求1启动并设置服务定位器。之后,请求2启动并使用它自己的容器实例覆盖服务定位器。之后,请求1中的代码调用ServiceLocator.Current,现在返回第二个请求的容器。有时候(如果你很幸运)你会得到ObjectDisposedException。如果一个线程处理其另一个线程仍在(错误地)使用的容器,则会发生这种情况。但有时会解析属于不同Web请求的类型(在多个线程上使用不安全的实例),这显然很糟糕并导致各种奇怪和不正确的行为。例如,当您返回LINQ to SQL DataContext或Entity Framework ObjectContext实例时。通常应根据Web请求和can't be shared by multiple threads配置这些类。