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

问题描述

所以我有一个遗留的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进行属性注入,该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.

所以我在看其他选项,并发现了,这显然是从v2开始的框架。可以删除默认的* .aspx处理程序,并将其替换为使用自定义实现的HTTPHandlers web.config部分。

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.

所以这有效,但是有一个catch。您定义的 _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.

更新 Microsoft已经从.NET 4.0开始部分信任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).

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

09-25 20:19