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
上面代码调用链路是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) { Dictionarydict = new Dictionary (); 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 Http管道事件内容请搜索科站长以前的文章或继续浏览下面的相关文章希望大家以后多多支持科站长!
栏 目:ASP.NET
下一篇:ASP.NET Core 9.0 中新增的MapStaticAssets() 中间件详解
本文标题:Asp.net MVC中的Http管道事件为什么要以Application_开头(原因解析)
本文地址:https://www.fushidao.cc/wangluobiancheng/3297.html
您可能感兴趣的文章
- 03-31详解如何在.NET代码中使用本地部署的Deepseek语言模型
- 02-06.net core如何使用Nacos注册中心
- 01-28使用.NET8构建一个高效的时间日期帮助类
- 01-26.NET Core GC压缩(compact_phase)底层原理解析
- 01-24在ASP.NET中读写TXT文本文件的多种方法
- 01-24在ASP.NET中读写XML数据的多种方法
- 01-24.NET轻松实现Excel转PDF的三种方法详解
- 01-23.NET9 AOT部署方案详解
- 01-23.NET NativeAOT 用法指南
- 01-23iis部署前后端分离项目全过程(Vuet前端和.NET6后端)


阅读排行
推荐教程
- 03-31详解如何在.NET代码中使用本地部署的Deepseek语言模型
- 11-23移动互联网广告有哪些模式?
- 11-22.net 应对网站访问压力的方案总结
- 11-22详解ASP.NET提取多层嵌套json数据的方法
- 11-23网站投放广告如何达到最好的效果
- 11-22.net 应对网站访问压力的方案总结
- 11-23网站打开速度慢解决办法
- 11-23草根站长为什么喜欢做门户站
- 11-22ASP.NET MVC分页问题解决
- 11-22ASP.NET编程简单实现生成静态页面的方法