Webforms 和依赖注入

发布于 2024-12-28 09:38:15 字数 641 浏览 2 评论 0原文

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

我对 DI 有相当丰富的经验,并且非常倾向于构造函数注入而不是 setter 注入。如果您熟悉 Webform,您就会知道 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 结合使用?

I am in the process of introducing a Dependency Injection framework into an existing WebForms application (using Castle Windsor).

I have pretty deep experience with DI, and tend to very strongly favor constructor injection over setter injection. If you are familiar with Webforms, you know that the ASP.Net framework handles the construction of page and control objects, making true constructor injection impossible.

My current solution is to register the container in the Application_Start event of the Global.asax, and keep the container as a public static variable in Global as well. I then simply resolve each service that I need directly in the page or control when I need them. So at the top of each page, I end up with code like this:

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

Obviously, I don't like having all these references to the container scattered about my application or having my page/control dependencies be non-explicit, but I have not been able to find a better way.

Is there a more elegant solution for using DI with Webforms?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

抽个烟儿 2025-01-04 09:38:16

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

相反,请在 Global 类上创建静态 Resolve 方法。这完全隐藏了容器,并允许您交换实现,而无需更改网页中的任何内容。当您执行此操作时,按照 @Wiktor 的建议使用公共服务定位器没有任何优势。公共服务定位器只是不必抽象的事物的另一种抽象(因为您已经使用 Global.Resolve 抽象出了容器)。

不幸的是,对于 Web 表单来说,并没有什么好的方法可以做到这一点。对于简单注入器,我写了一个Web 表单集成指南,基本上描述了Global.Resolve 方法,还展示了一种测试是否可以创建 Page 类的方法。该指南也可用于其他 DI 容器。

顺便说一句,请记住,对于温莎城堡,您请求的所有内容都必须明确发布(注册解决发布模式)。这有点令人讨厌(IMO),并且与其他容器的工作方式不同,如果您没有正确执行此操作,则可能会成为内存泄漏的根源。

最后一点。 可以使用 Web 表单进行构造函数注入。嗯...有点,因为这将在使用默认构造函数创建 Form 之后使用反射调用重载的构造函数,所以这会导致 时间耦合

I agree with @DarinDimitrov that MVP is an interesting option. However, when working with a legacy application, rewriting an existing page to the MVP pattern is a hell of a job. In that case it might be better to start with the Service Locator pattern (but only in your UI classes) as you are already doing. However, do change one thing. Do not expose the chosen DI container to the application, as I expect you are doing with the Global.IoC property.

Instead, create a static Resolve<T> method on the Global class. This hides the container completely and allows you to swap implementations without having to change anything in your web pages. When you do this, there is no advantage in using the Common Service Locator as @Wiktor proposes. The Common Service Locator is just another abstraction for something that doesn't have to be abstracted (since you've already abstracted away the container using the Global.Resolve<T>).

Unfortunately with Web forms, there is not really any good way to do this. For Simple Injector, I wrote an integration guide for Web Forms that basically describes the use of the Global.Resolve<T> method, but also shows a way to tests if Page classes can be created. The guide can be used for other DI containers as well.

BTW, please keep in mind that with Castle Windsor, everything you request must be released explicitly (the Register Resolve Release pattern). This is a bit nasty (IMO) and differs from how other containers work and can be a source of memory leaks when you do not do this correctly.

Last note. It is possible to do constructor injection with Web Forms. Well... sort of, since this will call the overloaded constructor using reflection after the Form has been created using the default constructor, so this causes Temporal Coupling.

平定天下 2025-01-04 09:38:16

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

是的,MVP 模式 允许您在以下方面清楚地分离关注点: WebForms 应用程序。一旦实现了关注点分离和弱耦合,DI 就很容易了。

在 ASP.NET MVC 中这是内置的。

Is there a more elegant solution for using DI with Webforms?

Yeap, the MVP pattern allows you to have a clean separation of concerns in a WebForms application. And once you have separation of concerns and weak coupling, DI is easy.

And in ASP.NET MVC that's built-in.

人事已非 2025-01-04 09:38:16

要知道这已经很老了,但现在,从 .NET 4.7.2 开始,WebForms 中就有了 DI。关于本文: ASP.NET 博客:在 WebForms 应用程序中使用依赖注入

只需安装 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>();
}

希望有帮助。

Know that this is pretty old, but now, there is DI in WebForms starting in .NET 4.7.2. Regarding to this article: ASP.NET Blog: Use Dependency Injection In WebForms Application

Just install Microsoft.AspNet.WebFormsDependencyInjection.Unity package and registr your classes in Global.asax:

protected void Application_Start(object sender, EventArgs e)
{
    var container = this.AddUnity();

    container.RegisterType<IPopularMovie, MovieManager>();
    container.RegisterType<IMovieRepository, XmlMovieRepository>();
}

Hope it help.

独闯女儿国 2025-01-04 09:38:16

ASP.NET MVC 有 IDependencyResolver静态管理器类它可以让您获取并设置解析器。我不喜欢在 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。我还没有走上使用 Web 表单的道路,所以需要其他人来代替我:)(我已经在 MVC 土地上生活了大约一年)。

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

ASP.NET MVC has IDependencyResolver and a static manager class that lets you get and set the resolver. I didn't like the idea of referencing System.Web.Mvc in a web forms project, so I went with IServiceLocator, which does about the same thing:

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

Then in your Page class ...

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

Or wire up DI via constructor injection. I haven't gone down that road with web forms yet, so someone else will need to fill in for me :) (I've been living mostly in MVC land for about a year now).

My example uses Unity, but you should be able to adapt it to any other DI implementation fairly easily.

却一份温柔 2025-01-04 09:38:16

正如 @DarinDimitrov 所说,MVP 模式是将 DI/IOC 与 Webforms 结合使用的最佳选择。

您可以自行实施或使用现有框架。我听说过 Webforms MVP,但我还没有真正使用过它。

根据文档,它通过 Castle Windsor、Autofac 内置了对 DI 的支持和团结。它还为演示者提供基于约定的自动发现。

As @DarinDimitrov says the MVP pattern is the way to go in order to use DI/IOC with Webforms.

Either you can roll your own implementation or use an existing framework. I've heard good about Webforms MVP, but I haven't actually used it.

According to the docs, it has built in support for DI via Castle Windsor, Autofac and Unity. It also has convention based auto discovery for Presenters.

轮廓§ 2025-01-04 09:38:16

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

http://commonservicelocator.codeplex.com/

Actually, what you have just built is your own implementation of the Service Locator. But, almost for sure, an implementation already exists for a IoC framework of your choice.

http://commonservicelocator.codeplex.com/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文