当PageHandlerFactory和IHttpHandlerFactory存在时,为什么每个人都说ASP.NET webforms中的依赖注入很难? [英] Why does everyone say dependency injection in ASP.NET webforms is hard when PageHandlerFactory and IHttpHandlerFactory exist?

查看:25
本文介绍了当PageHandlerFactory和IHttpHandlerFactory存在时,为什么每个人都说ASP.NET webforms中的依赖注入很难?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我有一个旧的 webforms 站点,并且正在努力使其更易于维护.扔掉它并重写它不是一种选择.

So I have a legacy webforms site and am working on making it easier to maintain. Chucking it away and rewriting it isn't an option.

IoC 显然是它得到的第一件事,但这让我留下了服务定位器模式和糟糕的品味,以及是否可以做得更好的疑惑.

IoC is obviously one of the first things it got, but this leaves me with the service-locator pattern and a bad taste, and the wondering of whether it could be done better.

我在线和离线交谈过的各种人告诉我,我可以使用 HttpModule 进行属性注入,该模块扫描 Page 类以查找用 Inject 属性或类似属性装饰的属性,但这听起来像是反射命中(缓存,但仍然)在每个请求上.没有吸引力.

Various people I've talked to online and off tell me that I could do property-injection with an HttpModule that scans a Page class for properties decorated with an Inject attribute or similar, but that sounds like a Reflection hit (cached, but still) on every request. Not appealing.

所以我正在寻找其他选项,并遇到了 System.Web.IHttpHandlerFactory,它显然从 v2 开始就在框架中.可以在 httpHandlers web.config 部分删除默认的 *.aspx 处理程序并将其替换为使用自定义实现的处理程序.

So I was looking at other options, and came across System.Web.IHttpHandlerFactory, which has apparently been in the framework since v2. One can remove the default *.aspx handler and replace it with one that uses a custom implementation, in httpHandlers web.config section.

所以,我与之交谈过的人并不傻;我想我会在这里问.用基于 IoC 的实现替换 webforms PageHandlerFactory 有什么问题吗?

So, the people I've talked to aren't dumb; I thought I'd ask here. Are there any gotchas with replacing the webforms PageHandlerFactory with an IoC-based implementation...?

看起来它同时具有 CreateHandler 和 ReleaseHandler 方法,因此容器中与生活方式相关的内存泄漏保持对已创建组件的引用应该不成问题...

It looks like it has both a CreateHandler and ReleaseHandler method, so life-style related memory leaks from the container keeping a reference to created components shouldn't be a problem...

推荐答案

由于 ASP.NET 的设计方式,Page 类需要有一个默认构造函数.当你想使用构造函数注入时,有一种方法可以解决这个问题.您可以保护默认构造函数并添加一个接受依赖项的公共构造函数,如下所示:

Because of the way ASP.NET is designed, Page classes need to have a default constructor. When you want to use constructor injection, there is a way around this. You can make the default constructor protected and add a single public constructor that takes the dependencies as follows:

public partial class _Default : System.Web.UI.Page
{
    private IUserService service;

    protected _Default()
    {
    }

    public _Default(IUserService service)
    {
        this.service = service;
    }
}

这允许您创建自定义 PageHandlerFactory 并在构造函数中注入依赖项.

This allows you to create a custom PageHandlerFactory and inject the dependencies in the constructor.

所以这可行,但有一个问题.您定义的 _Default 类不是 ASP.NET 使用的实际类.ASP.NET 创建一个继承自 _Default 的新类.这个新类基于 .aspx 文件中的标记构建控件层次结构.这个类看起来有点像这样:

So this works, but there is a catch though. The _Default class you define is not the actual class ASP.NET uses. ASP.NET creates a new class that inherits from _Default. This new class builds a control hierarchy based on the markup in the .aspx file. This class looks a bit like this:

public class ASPGeneratedDefault : _Default
{
    public ASPGeneratedDefault() : base()
    {
    }

     protected override void OnPreInit(object s, EventArgs e)
     {
          // Building up control hierarchy.
     }
}

如您所见,ASP.NET 并未在 ASPGeneratedDefault 中覆盖自定义构造函数.因此,无法让 DI 框架为我们创建这种类型.解决这个问题的方法是让 ASP.NET 为我们创建这种类型,并在该现有实例上调用 _Default 基类的非默认构造函数.因为这个实例已经存在,我们必须用反射来做这件事,这在部分信任下运行时会失败.

As you can see, the custom constructor hasn't been overridden in the ASPGeneratedDefault by ASP.NET. Because of this there is no way of letting a DI framework create this type for us. The way around this is to let ASP.NET create this type for us and invoke the non-default constructor of the _Default base class on that existing instance. Because this instance already exists, we must do this with reflection and this will fail when run in partial trust.

除此之外,这适用于页面类,但不适用于页面上的用户控件.ASP.NET 的代码生成器在控件层次结构构建过程中使用它们的默认构造函数更新这些控件.当您希望它为它们工作时,您需要自定义 PageHandlerFactory 来挂钩这些控件的 PreInit 事件,因为在构造页面类期间,相关的控件和用户控件尚未创建.但是,要在它们上注册 PreInit 事件,您需要在页面类中找到这些控件,并且我们需要再次反映页面类.由于控件存储在非公共实例字段中,因此这在部分信任中也不起作用.

Besides that, this works for page classes, but not for user controls on the page. The code generator of ASP.NET news up those controls with their default constructor during the control hierarchy build up process. When you want this to work for them, you need your custom PageHandlerFactory to hook to the PreInit event of those controls, because during the time the page class is constructed, the related controls and user controls are not yet created. However, to register the PreInit event on them, you need to find those controls within the page class, and again we need to reflect over the page class. Because controls are stored in non-public instance fields, again this won't work in partial trust.

是否是您的应用程序无法在部分信任下运行的问题取决于您,但由于 .NET 4 的安全模型已大大简化,因此在部分信任下运行 Web 应用程序非常容易,并且是我努力去做的事情.

Whether or not it is a problem your application can not run in partial trust is up to you, but since the security model of .NET 4 has been simplified considerably, it is very easy to run web apps in partial trust and it is something I strive to do.

TLDR;所以总而言之,可以这样做(例如参见这个例子),但由于 ASP.NET Web 窗体框架的限制,您需要在完全信任的情况下运行才能使其工作.

TLDR; So in conclusion, it is possible to do so (see for instance this example), but because of the limitations of the ASP.NET Web Forms framework, you need to run in full trust to get it to work.

更新 从 NET 4.0 开始,Microsoft 已过时对 ASP.NET 的部分信任(阅读 此处).因此,从这个角度来看,远离完全信任可能没有那么有用(无论如何你都会需要它).

UPDATE Microsoft has obsoleted partial trust for ASP.NET starting with NET 4.0 (read here). So from that point of view, keeping away from full trust might not be that useful (you'll need it anyway).

这篇关于当PageHandlerFactory和IHttpHandlerFactory存在时,为什么每个人都说ASP.NET webforms中的依赖注入很难?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆