今天遇到一个问题,需要在API请求结束时,释放数据库链接,避免连接池被爆掉。
按照以往的经验,需要实现IHttpModule,具体不展开了。
但是实现了IHttpModule后,还得去web.config中增加配置,这有点麻烦了,就想有没有简单的办法。
其实是有的,就是在Global.asax.cs里面定义并实现 Application_EndRequest 方法,在这个方法里面去释放数据库连接即可,经过测试,确实能达到效果。
但是,为什么方法名必须是Application_EndRequest ?在这之前真不知道为什么,只知道baidu上是这么说的,也能达到效果。
还好我有一点好奇心,想搞清楚是怎么回事情,就把net framework的源码拉下来(其实源代码在电脑里面已经躺了N年了) 分析了一下,以下是分析结果。
省略掉前面N个调用
第一个需要关注的是 HttpApplicationFactory.cs
从名字就知道,这是HttpApplication的工厂类,大家看看Gloabal.asax.cs 里面,是不是这样定义的
1 2 3 4 | public class MvcApplication : System.Web.HttpApplication { ..... } |
两者结合起来看,可以推测,HttpApplicationFactory 是用来获取 MvcApplication 实例的,实际情况也是如此 上代码(来自HttpApplicationFactory)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | 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 data-origwidth="" data-origheight="" style="width: 1264px;"> 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 data-origwidth="" data-origheight="" style="width: 899px;"> 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 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 ; } }</ object ></ object > |
上面代码调用链路是EnsureInited->Init->CompileApplication->ReflectOnApplicationType->ReflectOnMethodInfoIfItLooksLikeEventHandler ,核心作用是:将MvcApplication中,方法名包含下划线、方法参数为空或者有2个参数(第一个参数的类型是Object,第二个参数的类型是EventArgs) 的方法加入到_eventHandlerMethods 中
那么事件是怎么绑定的呢?继续上代码
1 2 3 4 5 6 7 8 9 10 11 12 | internal class HttpApplicationFactory{ ...... using ( new ApplicationImpersonationContext()) { app.InitInternal(context, _state, _eventHandlerMethods); } ...... ...... using ( new ApplicationImpersonationContext()) { app.InitInternal(context, _state, _eventHandlerMethods); } ...... } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // 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 dict = new Dictionary(); BuildEventMaskDictionary(dict); if ( null == _pipelineEventMasks) { _pipelineEventMasks = dict; } } for ( int i = 0; i |
核心方法:HookupEventHandlersForApplicationAndModules,其作用就是将前面获取到的method与HttpApplication的Event进行绑定(前提是方法名是以Application_开头的),
后面就是向IIS注册事件通知了,由于看不到IIS源码,具体怎么做的就不知道了。
最后安利一下,还是用net core吧,更加清晰、直观,谁用谁知道。
到此这篇关于Asp.net MVC中的Http管道事件为什么要以Application_开头的文章就介绍到这了,更多相关Asp.net MVC Http管道事件内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!