Asp.net MVC中的Http管道事件为什么要以Application_开头?
今天遇到一个问题,需要在API请求结束时,释放数据库链接,避免连接池被爆掉。
按照以往的经验,需要实现IHttpModule,具体不展开了。
但是实现了IHttpModule后,还得去web.config中增加配置,这有点麻烦了,就想有没有简单的办法。
其实是有的,就是在Global.asax.cs里面定义并实现 Application_EndRequest 方法,在这个方法里面去释放数据库连接即可,经过测试,确实能达到效果。
但是,为什么方法名必须是Application_EndRequest ?在这之前真不知道为什么,只知道baidu上是这么说的,也能达到效果。
还好我有一点好奇心,想搞清楚是怎么回事情,就把net framework的源码拉下来(其实源代码在电脑里面已经躺了N年了) 分析了一下,以下是分析结果。
省略掉前面N个调用
第一个需要关注的是 HttpApplicationFactory.cs
从名字就知道,这是HttpApplication的工厂类,大家看看Gloabal.asax.cs 里面,是不是这样定义的
public class MvcApplication : System.Web.HttpApplication
{
.....
}
两者结合起来看,可以推测,HttpApplicationFactory 是用来获取 MvcApplication 实例的,实际情况也是如此 上代码(来自HttpApplicationFactory)
internal class HttpApplicationFactory{
internal const string applicationFileName = "global.asax"; //看到这里,就知道为什么入口文件是global.asax了,因为这里定义死了
...
private void EnsureInited() {
if (!_inited) {
lock (this) {
if (!_inited) {
Init();
_inited = true;
}
}
}
}
private void Init() {
if (_customApplication != null)
return;
try {
try {
_appFilename = GetApplicationFile();
CompileApplication();
}
finally {
// Always set up global.asax file change notification, even if compilation
// failed. This way, if the problem is fixed, the appdomain will be restarted.
SetupChangesMonitor();
}
}
catch { // Protect against exception filters
throw;
}
}
private void CompileApplication() {
// Get the Application Type and AppState from the global file
_theApplicationType = BuildManager.GetGlobalAsaxType();
BuildResultCompiledGlobalAsaxType result = BuildManager.GetGlobalAsaxBuildResult();
if (result != null) {
// Even if global.asax was already compiled, we need to get the collections
// of application and session objects, since they are not persisted when
// global.asax is compiled. Ideally, they would be, but since <object> tags
// are only there for ASP compat, it's not worth the trouble.
// Note that we only do this is the rare case where we know global.asax contains
// <object> tags, to avoid always paying the price (VSWhidbey 453101)
if (result.HasAppOrSessionObjects) {
GetAppStateByParsingGlobalAsax();
}
// Remember file dependencies
_fileDependencies = result.VirtualPathDependencies;
}
if (_state == null) {
_state = new HttpApplicationState();
}
// Prepare to hookup event handlers via reflection
ReflectOnApplicationType();
}
private void ReflectOnApplicationType() {
ArrayList handlers = new ArrayList();
MethodInfo[] methods;
Debug.Trace("PipelineRuntime", "ReflectOnApplicationType");
// get this class methods
methods = _theApplicationType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
foreach (MethodInfo m in methods) {
if (ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
handlers.Add(m);
}
// get base class private methods (GetMethods would not return those)
Type baseType = _theApplicationType.BaseType;
if (baseType != null && baseType != typeof(HttpApplication)) {
methods = baseType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
foreach (MethodInfo m in methods) {
if (m.IsPrivate && ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
handlers.Add(m);
}
}
// remember as an array
_eventHandlerMethods = new MethodInfo[handlers.Count];
for (int i = 0; i < _eventHandlerMethods.Length; i++)
_eventHandlerMethods[i] = (MethodInfo)handlers[i];
}
private bool ReflectOnMethodInfoIfItLooksLikeEventHandler(MethodInfo m) {
if (m.ReturnType != typeof(void))
return false;
// has to have either no args or two args (object, eventargs)
ParameterInfo[] parameters = m.GetParameters();
switch (parameters.Length) {
case 0:
// ok
break;
case 2:
// param 0 must be object
if (parameters[0].ParameterType != typeof(System.Object))
return false;
// param 1 must be eventargs
if (parameters[1].ParameterType != typeof(System.EventArgs) &&
!parameters[1].ParameterType.IsSubclassOf(typeof(System.EventArgs)))
return false;
// ok
break;
default:
return false;
}
// check the name (has to have _ not as first or last char)
String name = m.Name;
int j = name.IndexOf('_');
if (j <= 0 || j > name.Length-1)
return false;
// special pseudo-events
if (StringUtil.EqualsIgnoreCase(name, "Application_OnStart") ||
StringUtil.EqualsIgnoreCase(name, "Application_Start")) {
_onStartMethod = m;
_onStartParamCount = parameters.Length;
}
else if (StringUtil.EqualsIgnoreCase(name, "Application_OnEnd") ||
StringUtil.EqualsIgnoreCase(name, "Application_End")) {
_onEndMethod = m;
_onEndParamCount = parameters.Length;
}
else if (StringUtil.EqualsIgnoreCase(name, "Session_OnEnd") ||
StringUtil.EqualsIgnoreCase(name, "Session_End")) {
_sessionOnEndMethod = m;
_sessionOnEndParamCount = parameters.Length;
}
return true;
}
}
上面代码调用链路是EnsureInited->Init->CompileApplication->ReflectOnApplicationType->ReflectOnMethodInfoIfItLooksLikeEventHandler ,核心作用是:将MvcApplication中,方法名包含下划线、方法参数为空或者有2个参数(第一个参数的类型是Object,第二个参数的类型是EventArgs) 的方法加入到_eventHandlerMethods 中
那么事件是怎么绑定的呢?继续上代码
internal class HttpApplicationFactory{
......
using (new ApplicationImpersonationContext()) {
app.InitInternal(context, _state, _eventHandlerMethods);
}
......
......
using (new ApplicationImpersonationContext()) {
app.InitInternal(context, _state, _eventHandlerMethods);
}
......
}
// HttpApplication.cs
public class HttpApplication{
internal void InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) {
.....
if (handlers != null) {
HookupEventHandlersForApplicationAndModules(handlers);
}
.....
}
internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers) {
.....
if (handlers != null)
HookupEventHandlersForApplicationAndModules(handlers);
.....
}
private void HookupEventHandlersForApplicationAndModules(MethodInfo[] handlers) {
_currentModuleCollectionKey = HttpApplicationFactory.applicationFileName;
if(null == _pipelineEventMasks) {
Dictionary<string, RequestNotification> dict = new Dictionary<string, RequestNotification>();
BuildEventMaskDictionary(dict);
if(null == _pipelineEventMasks) {
_pipelineEventMasks = dict;
}
}
for (int i = 0; i < handlers.Length; i++) {
MethodInfo appMethod = handlers[i];
String appMethodName = appMethod.Name;
int namePosIndex = appMethodName.IndexOf('_');
String targetName = appMethodName.Substring(0, namePosIndex);
// Find target for method
Object target = null;
if (StringUtil.EqualsIgnoreCase(targetName, "Application"))
target = this;
else if (_moduleCollection != null)
target = _moduleCollection[targetName];
if (target == null)
continue;
// Find event on the module type
Type targetType = target.GetType();
EventDescriptorCollection events = TypeDescriptor.GetEvents(targetType);
string eventName = appMethodName.Substring(namePosIndex+1);
EventDescriptor foundEvent = events.Find(eventName, true);
if (foundEvent == null
&& StringUtil.EqualsIgnoreCase(eventName.Substring(0, 2), "on")) {
eventName = eventName.Substring(2);
foundEvent = events.Find(eventName, true);
}
MethodInfo addMethod = null;
if (foundEvent != null) {
EventInfo reflectionEvent = targetType.GetEvent(foundEvent.Name);
Debug.Assert(reflectionEvent != null);
if (reflectionEvent != null) {
addMethod = reflectionEvent.GetAddMethod();
}
}
if (addMethod == null)
continue;
ParameterInfo[] addMethodParams = addMethod.GetParameters();
if (addMethodParams.Length != 1)
continue;
// Create the delegate from app method to pass to AddXXX(handler) method
Delegate handlerDelegate = null;
ParameterInfo[] appMethodParams = appMethod.GetParameters();
if (appMethodParams.Length == 0) {
// If the app method doesn't have arguments --
// -- hookup via intermidiate handler
// only can do it for EventHandler, not strongly typed
if (addMethodParams[0].ParameterType != typeof(System.EventHandler))
continue;
ArglessEventHandlerProxy proxy = new ArglessEventHandlerProxy(this, appMethod);
handlerDelegate = proxy.Handler;
}
else {
// Hookup directly to the app methods hoping all types match
try {
handlerDelegate = Delegate.CreateDelegate(addMethodParams[0].ParameterType, this, appMethodName);
}
catch {
// some type mismatch
continue;
}
}
// Call the AddXXX() to hook up the delegate
try {
addMethod.Invoke(target, new Object[1]{handlerDelegate});
}
catch {
if (HttpRuntime.UseIntegratedPipeline) {
throw;
}
}
if (eventName != null) {
if (_pipelineEventMasks.ContainsKey(eventName)) {
if (!StringUtil.StringStartsWith(eventName, "Post")) {
_appRequestNotifications |= _pipelineEventMasks[eventName];
}
else {
_appPostNotifications |= _pipelineEventMasks[eventName];
}
}
}
}
}
}
核心方法:HookupEventHandlersForApplicationAndModules,其作用就是将前面获取到的method与HttpApplication的Event进行绑定(前提是方法名是以Application_开头的),
后面就是向IIS注册事件通知了,由于看不到IIS源码,具体怎么做的就不知道了。
最后安利一下,还是用net core吧,更加清晰、直观,谁用谁知道。
Asp.net MVC中的Http管道事件为什么要以Application_开头?的更多相关文章
- ASP.NET MVC自定义Module记录管道事件执行顺序
1. 在Visual Studio 新建项目,模板为空,下面结构选择MVC. 2. 在项目中新建一个类MyModule,实现IHttpModule接口 namespace SimpleApp.Infr ...
- Asp.Net MVC 的19个管道事件
httpApplication调用ProcessRequest方法,内部执行19个管道事件,如下 BeginRequest 开始处理请求 AuthenticateRequest 授权验证请求开始,获 ...
- Asp.net mvc 中的 Controller 的激活
Controller 激活是指根据路由系统解析出来的 Controller 的名称创建 控制器(Controller)的过程,这里的控制器泛指实现了 IController 接口的类型 激活过程中的核 ...
- 最新 .NET Core 中 WebSocket的使用 在Asp.Net MVC 中 WebSocket的使用 .NET Core 中 SignalR的使用
ASP.NET MVC 中使用WebSocket 笔记 1.采用控制器的方法 这个只写建立链接的方法的核心方法 1.1 踩坑 网上都是直接 传个异步方法 直接接受链接 自己尝试了好多次链接是打开的,到 ...
- 在 ASP.NET MVC 中充分利用 WebGrid (microsoft 官方示例)
在 ASP.NET MVC 中充分利用 WebGrid https://msdn.microsoft.com/zh-cn/magazine/hh288075.aspx Stuart Leeks 下载代 ...
- Asp.net mvc 中Action 方法的执行(一)
[toc] 在 Aps.net mvc 应用中对请求的处理最终都是转换为对某个 Controller 中的某个 Action 方法的调用,因此,要对一个请求进行处理,第一步,需要根据请求解析出对应的 ...
- ASP.NET MVC中注册Global.asax的Application_Error事件处理全局异常
在ASP.NET MVC中,通过应用程序生命周期中的Application_Error事件可以捕获到网站引发的所有未处理异常.本文作为学习笔记,记录了使用Global.asax文件的Applicati ...
- ASP.NET MVC中jQuery与angularjs混合应用传参并绑定数据
要求是这样子的,在一个列表页中,用户点击详细铵钮,带记录的主键值至另一页.在另一外页中,获取记录数据,然后显示此记录数据在网页上. 先用动图演示: 昨天有分享为ng-click传递参数 <ang ...
- 在ASP.NET MVC 中使用ActiveReports报表控件
随着MVC模式的广泛运用,对Web应用系统的开发带来了巨大的影响,我们好像又回到了原来的ASP时代,视乎这是一种后退而不是一种进步,不过MVC模式给我们带来的影响不仅限于我们所看到的这一点..MVC看 ...
- ASP.NET MVC中的Session以及处理方式
最近在ASP.NET MVC项目中碰到这样的情况:在一个controller中设置了Session,但在另一个controller的构造函数中无法获取该Session,会报"System.N ...
随机推荐
- 合合信息参编“生成式人工智能个人信息保护技术要求系列标准”,助力AI行业可信发展
生成式人工智能作为新一轮的技术革命成果,在赋能千行百业,给经济社会发展带来新机遇的同时,也产生了个人信息泄露.数据安全风险等问题.在此背景下,中国信息通信研究院(简称"中国信通院" ...
- HTML – script async defer
参考 Youtube – #3 JavaScript Loading Strategies (async and defer) | JavaScript Full Tutorial no async ...
- JSP——简介-快速入门
JSP 简介 JSP 快速入门 <%@ page contentType="text/html;charset=UTF-8" language=&qu ...
- QT6 QML编程
QT6 QML编程 使用AI技术辅助生成 QT界面美化视频课程 QT性能优化视频课程 QT原理与源码分析视频课程 QT QML C++扩展开发视频课程 免费QT视频课程 您可以看免费1000+个QT技 ...
- [R18][中国語翻訳]HDKのABC370赛試(ABC370)
A.Raise Both Hands \(\texttt{Diff }11\) #include<bits/stdc++.h> using namespace std; #define e ...
- [TK] 盖房子 hzoi-tg#262
同机房大佬也写了这题的 题解. 通解分析 此类问题我通常喜欢归纳成一类,即阻碍联通的坐标DP. 既然是阻碍联通,那么此类问题的通用思路是这样的: 首先将dp数组图形化. 一般用一个特定图形上的特定点来 ...
- [Tkey] OSU!
更新的题解可看 此处 你说得对但是 恐怖日本病毒会自动向你的电脑中下载 OSU! 题意简述 一个 01 串,每个位置有 \(p_{i}\) 的概率为 \(1\),连续的 \(x\) 个 \(1\) 贡 ...
- Android复习(五)设备兼容—>多apk支持
1. 对于不同的屏幕发布单独的apk https://developer.android.google.cn/training/multiple-apks/screensize 2.多窗口模式 在An ...
- 使用 Cilium 作为网络插件部署 K8s + KubeSphere
Cilium 简介 Cilium 是一个用于容器网络领域的开源项目,主要是面向容器而使用,用于提供并透明地保护应用程序工作负载(如应用程序容器或进程)之间的网络连接和负载均衡. Cilium 在第 3 ...
- Oracle 数据泵 定时全库备份&&删除备份文件【注意点】
一.概述 在给客户部署的数据泵备份&&删除过期备份脚本时,脚本删除部分未生效,导致存储空间占用非常大. 手动执行该删除命令时,执行成功: 最后发现需要增加 find.rm 等命令的PA ...