【问题标题】:Webforms and Dependency InjectionWebforms 和依赖注入
【发布时间】:2012-02-15 08:57:45
【问题描述】:

我正在将依赖注入框架引入现有的 WebForms 应用程序(使用 Castle Windsor)。

我在 DI 方面有相当丰富的经验,并且倾向于非常喜欢构造函数注入而不是 setter 注入。如果您熟悉 Webforms,您就会知道 ASP.Net 框架处理页面和控件对象的构造,因此无法实现真正​​的构造函数注入。

我目前的解决方案是在 Global.asax 的 Application_Start 事件中注册容器,并将容器作为公共静态变量保存在 Global 中。然后,我只需在页面中直接解决我需要的每个服务,或者在需要它们时进行控制。所以在每一页的顶部,我都会得到这样的代码:

private readonly IMyService _exposureManager = Global.IoC.Resolve<IMyService>();
private readonly IMyOtherService _tenCustomersExposureManager = Global.IoC.Resolve<IMyOtherService>();

显然,我不喜欢所有这些对容器的引用分散在我的应用程序中,或者我的页面/控件依赖项是非显式的,但我一直无法找到更好的方法。

有没有更优雅的解决方案将 DI 与 Webforms 结合使用?

【问题讨论】:

  • 我找到了这篇文章codemag.com/Article/1210031,其中有关于如何在 WPF、MVC 和 WebForms 上实现依赖注入的优秀示例代码(在 WebForms 的情况下使用 PageHandlerFactory)。似乎说由于代码隐藏类的限制,您将不得不使用 setter 注入。有趣的是,它还展示了 Microsoft Managed Extensibility Framework (MEF) 如何帮助您以非常有用且略微不标准的方式解决此问题和类似的 DI 问题。
  • 从 .NET 4.7.2 开始,WebForms 中有一个依赖注入。请参阅下面的答案。

标签: c# asp.net dependency-injection webforms castle-windsor


【解决方案1】:

有没有更优雅的解决方案将 DI 与 Webforms 结合使用?

是的,MVP pattern 允许您在 WebForms 应用程序中清晰地分离关注点。而且一旦有了关注点分离和弱耦合,DI 就很容易了。

在 ASP.NET MVC 中是内置的。

【讨论】:

    【解决方案2】:

    实际上,您刚刚构建的是您自己的服务定位器实现。但是,几乎可以肯定,您选择的 IoC 框架的实现已经存在。

    http://commonservicelocator.codeplex.com/

    【讨论】:

      【解决方案3】:

      ASP.NET MVC 具有IDependencyResolverstatic manager class,可让您获取和设置解析器。我不喜欢在 Web 表单项目中引用 System.Web.Mvc 的想法,所以我选择了 IServiceLocator,它的作用大致相同:

      public static class Bootstrapper
      {
          private static readonly IUnityContainer _container = new UnityContainer();
      
          public static void Initialize()
          {
              ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(_container));
      
              _container.RegisterType<IDriverService, DriverService>();
          }
      
          public static void TearDown()
          {
              _container.Dispose();
          }
      }
      
      public class Global : HttpApplication
      {
          protected void Application_Start(object sender, EventArgs e)
          {
              Bootstrapper.Initialize();
          }
      
          protected void Application_End(object sender, EventArgs e)
          {
              Bootstrapper.TearDown();
          }
      }
      

      然后在您的 Page 类中...

      IDriverService service = ServiceLocator.Current.GetInstance<IDriverService>();
      

      或者通过构造函数注入连接 DI。我还没有通过网络表单走这条路,所以需要其他人来为我填写:)(我已经在 MVC 领域生活了大约一年)。

      我的示例使用 Unity,但您应该能够相当轻松地将其适应任何其他 DI 实现。

      【讨论】:

      • 在应用程序端做TearDown真的有用吗?无论如何,应用程序域正在被卸载。仅当您在其 Dispose 方法中注册了具有拆卸逻辑的单例服务时,它才有用,但仍然很脆弱,因为无法保证所有这些 Dispose 方法在 AppDomain 卸载时实际运行。
      • 好问题。我只是想遵循一次性模式。除了应用程序结束之外,我不知道还能把它放在哪里。
      【解决方案4】:

      正如@DarinDimitrov 所说,MVP 模式是将 DI/IOC 与 Webforms 结合使用的方法。

      您可以推出自己的实现或使用现有框架。我听说过Webforms MVP,但实际上我并没有使用它。

      根据the docs,它通过 Castle Windsor、Autofac 和 Unity 内置了对 DI 的支持。它还为演示者提供基于约定的自动发现功能。

      【讨论】:

        【解决方案5】:

        我同意@DarinDimitrov 的观点,MVP 是一个有趣的选择。但是,在使用遗留应用程序时,将现有页面重写为 MVP 模式是一项艰巨的工作。在这种情况下,最好从服务定位器模式开始(但在您的 UI 类中),就像您已经在做的那样。但是,一定要改变一件事。不要将所选的 DI 容器暴露给应用程序,正如我希望您使用 Global.IoC 属性所做的那样。

        相反,在Global 类上创建一个静态Resolve&lt;T&gt; 方法。这完全隐藏了容器,并允许您交换实现而无需更改网页中的任何内容。当您这样做时,使用 @Wiktor 建议的公共服务定位器没有任何优势。 Common Service Locator 只是对不需要抽象的东西的另一种抽象(因为您已经使用 Global.Resolve&lt;T&gt; 抽象出容器)。

        不幸的是,对于 Web 表单,实际上并没有什么好的方法可以做到这一点。对于Simple Injector,我写了一个integration guide for Web Forms,它基本上描述了Global.Resolve&lt;T&gt; 方法的使用,但也展示了一种测试是否可以创建Page 类的方法。该指南也可用于其他 DI 容器。

        顺便说一句,请记住,对于温莎城堡,您要求的所有内容都必须明确发布(Register Resolve Release pattern)。这有点讨厌 (IMO),并且与其他容器的工作方式不同,如果您没有正确执行此操作,可能会导致内存泄漏。

        最后一点。 It is possible to do constructor injection with Web Forms。嗯...有点,因为这将在使用默认构造函数创建Form 之后使用反射调用重载构造函数,所以这会导致Temporal Coupling

        【讨论】:

        • 感谢您的回答。最后一篇文章很有趣,如果不是完全信任问题,确实会引起极大的兴趣。
        • @PhilSandler:微软似乎有abandoned partial trust completely from ASP.NET 4.0 及以上。
        • 如果Resolve&lt;T&gt; 方法是静态的,那么它就无法访问实例成员容器。不过我可能误会你了。您是否建议也将容器设为静态?
        • 如果您可以升级到 .net 框架 4.7.2,则支持依赖注入。 integrating SimpleInjector into webforms 的示例。
        【解决方案6】:

        知道这已经很老了,但是现在,从 .NET 4.7.2 开始,WebForms 中就有了 DI。关于这篇文章:ASP.NET Blog: Use Dependency Injection In WebForms Application

        只需安装Microsoft.AspNet.WebFormsDependencyInjection.Unity 包并在Global.asax 中注册您的课程:

        protected void Application_Start(object sender, EventArgs e)
        {
            var container = this.AddUnity();
        
            container.RegisterType<IPopularMovie, MovieManager>();
            container.RegisterType<IMovieRepository, XmlMovieRepository>();
        }
        

        希望对您有所帮助。

        【讨论】: