Application_Start 与 OnInit 与构造函数

Posted

技术标签:

【中文标题】Application_Start 与 OnInit 与构造函数【英文标题】:Application_Start versus OnInit versus constructor 【发布时间】:2010-07-23 12:31:54 【问题描述】:

自从我几年前开始编写经典的 ASP 12(左右)以来,我一直在考虑这个问题,但我从来没有找到一个很好的解决方案,因为 ASP 和 ASP.NET 的体系结构一直是一大堆不好的做法, 魔术共享单例等。我最大的问题是 HttpApplication 对象及其非事件事件(Application_StartApplication_End 等)。

如果您想在 HTTP 应用程序的整个生命周期内只做一次事情,Application_Start 是显而易见的地方。对?不完全是。首先,这本身不是一个事件,它是一个神奇的命名约定,如果遵循该约定,该方法会在 IIS 创建的每个 AppDomain 中调用一次。

除了魔法命名约定是一种可怕的做法之外,我开始认为这可能是HttpApplication 对象上不存在Start 事件之类的原因。所以我对确实存在的事件进行了试验,例如Init。嗯,这也不是一个真正的事件,它是一个可覆盖的方法,这是次优的。

似乎Init() 方法在HttpApplication 对象的每个实例化时都被调用,每个AppDomain 不止一次发生这种情况。这意味着我不妨把我的启动逻辑放在HttpApplication 对象的构造函数中。

现在我的问题是,为什么我不应该将启动逻辑放在构造函数中?为什么连Init() 都存在,我需要关心Application_Start 吗?如果我这样做了,谁能解释为什么HttpApplication 对象中没有针对此伪事件的适当事件或可覆盖方法?

任何人都可以向我解释为什么在一个典型的 ASP.NET 应用程序中创建了我的 HttpApplication 的 8 个实例(这当然会导致构造函数和 Init 运行同样多次;这可以得到缓解当我的应用程序只有一个 AppDomain 时,使用锁定和一个名为 initialized 的共享静态布尔值?

【问题讨论】:

可能需要在 Application_Start 中完成,以便框架可以设置所有这些神奇的共享对象。可能在此之前类已被触及,而静态构造函数运行得太早了。 @JoeKoberg,这是一个很好的观点。当然,与显式方法调用相比,您对何时调用静态构造函数的控制更少。不过,我仍然认为不需要Init()Application_StartApplication_End 应该仍然是正确的事件。 【参考方案1】:

Asp.Net 运行时保留一个 HttpApplication 对象池。每个 .aspx 请求都由从池中分配的单个对象处理(在您的情况下为 8 个对象)。

您的问题的答案,确实调用了 Application_Start 事件,但仅针对 HttpApplication 的第一个实例,而不是后续实例,因此您可以确保在您的应用程序启动或应用程序池的应用程序池中只调用一次IIS 重新启动。 Application_OnEnd 事件(最后一个实例)也是如此

同时,在 HttpApplication 对象的每个实例上调用 Init() 和 Dispose()。这将在每个实例(也就是每个请求)上调用。

他们为什么要那样做..?也许是为了平衡性能和内存优化。

希望我回答了你的问题。

【讨论】:

感谢您的回答。不过,您没有在回复中提到构造函数。当你可以在构造函数中做同样的事情并且将私有字段更改为readonly 的额外好处时,为什么还需要 Init()?为什么Application_StartApplication_End 不是可以与适当的事件处理程序挂钩的适当事件?另外,每个工作进程、每个应用程序域会调用一次这些吗? 每个工作进程一次,对吧?因为您可以在自己的工作进程中创建 AppDomain,而不会触发 Application_Start。通常,如果您不创建自己的应用程序域,那么是的,它应该是每个 AppDomain 以及工作进程,afaik。【参考方案2】:

在第一次创建 HttpApplication 实例时调用 Application_Start,但在后续实例中不调用似乎有点 hack。也许微软不想向那些不想知道的人解释静态构造函数的概念。

但是,

Application_End() 似乎是必需的,因为没有 C# 等效的静态析构函数/终结器。随着黑客的发展,这还不错。只是闻起来有点好笑。

【讨论】:

我同意 Application_End() 是必需的,但为什么它(也不是 Application_Start)不是您可以收听的适当事件?我了解与经典 ASP 的背景兼容性,但不公开事件对我来说没有多大意义。 我认为我们拥有Application_Start 的原因可能是您无法在静态构造函数中执行的初始化 - 例如,如果您正在初始化的内容具有依赖关系关于配置值或其他内容,在调用静态构造函数时尚不可用。这可能就是原因。 (无论哪种方式,这都是一个令人困惑的混乱。?)【参考方案3】:

为每个并发请求创建一个 HttpApplication 对象。也就是说,ASP.NET 创建的每个线程都有自己的 HttpApplication 实例。实例被重用于后续请求,就像重用线程池中的线程一样。

使用 Init 方法初始化 HttpApplication 上的实例字段,因为如果在 Application_Start 事件中完成,这些字段只会在第一个实例中被初始化。

【讨论】:

但是为什么要使用 Init() 方法而不是构造函数呢? Init() 方法提供了哪些构造函数没有提供的功能?

以上是关于Application_Start 与 OnInit 与构造函数的主要内容,如果未能解决你的问题,请参考以下文章

关于Application_End 与 Application_Start事件触发情况的测试(待续)

为啥 Application_Start 从不同的线程调用两次?

无法中断 global.asax / Application_Start

asp.net的Application_Start的一些疑问

FXCop 正在捕获 Application_Start() 事件,因为它不是静态的

<IDENTITY IMPERSONATE=TRUE> & Application_Start 事件