今天遇到一个问题,需要在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_开头?的更多相关文章

  1. ASP.NET MVC自定义Module记录管道事件执行顺序

    1. 在Visual Studio 新建项目,模板为空,下面结构选择MVC. 2. 在项目中新建一个类MyModule,实现IHttpModule接口 namespace SimpleApp.Infr ...

  2. Asp.Net MVC 的19个管道事件

    httpApplication调用ProcessRequest方法,内部执行19个管道事件,如下 BeginRequest  开始处理请求 AuthenticateRequest 授权验证请求开始,获 ...

  3. Asp.net mvc 中的 Controller 的激活

    Controller 激活是指根据路由系统解析出来的 Controller 的名称创建 控制器(Controller)的过程,这里的控制器泛指实现了 IController 接口的类型 激活过程中的核 ...

  4. 最新 .NET Core 中 WebSocket的使用 在Asp.Net MVC 中 WebSocket的使用 .NET Core 中 SignalR的使用

    ASP.NET MVC 中使用WebSocket 笔记 1.采用控制器的方法 这个只写建立链接的方法的核心方法 1.1 踩坑 网上都是直接 传个异步方法 直接接受链接 自己尝试了好多次链接是打开的,到 ...

  5. 在 ASP.NET MVC 中充分利用 WebGrid (microsoft 官方示例)

    在 ASP.NET MVC 中充分利用 WebGrid https://msdn.microsoft.com/zh-cn/magazine/hh288075.aspx Stuart Leeks 下载代 ...

  6. Asp.net mvc 中Action 方法的执行(一)

    [toc] 在 Aps.net mvc 应用中对请求的处理最终都是转换为对某个 Controller 中的某个 Action 方法的调用,因此,要对一个请求进行处理,第一步,需要根据请求解析出对应的 ...

  7. ASP.NET MVC中注册Global.asax的Application_Error事件处理全局异常

    在ASP.NET MVC中,通过应用程序生命周期中的Application_Error事件可以捕获到网站引发的所有未处理异常.本文作为学习笔记,记录了使用Global.asax文件的Applicati ...

  8. ASP.NET MVC中jQuery与angularjs混合应用传参并绑定数据

    要求是这样子的,在一个列表页中,用户点击详细铵钮,带记录的主键值至另一页.在另一外页中,获取记录数据,然后显示此记录数据在网页上. 先用动图演示: 昨天有分享为ng-click传递参数 <ang ...

  9. 在ASP.NET MVC 中使用ActiveReports报表控件

    随着MVC模式的广泛运用,对Web应用系统的开发带来了巨大的影响,我们好像又回到了原来的ASP时代,视乎这是一种后退而不是一种进步,不过MVC模式给我们带来的影响不仅限于我们所看到的这一点..MVC看 ...

  10. ASP.NET MVC中的Session以及处理方式

    最近在ASP.NET MVC项目中碰到这样的情况:在一个controller中设置了Session,但在另一个controller的构造函数中无法获取该Session,会报"System.N ...

随机推荐

  1. Angular 18+ 高级教程 – Component 组件 の Angular Component vs Web Component

    前言 在 初识 Angular 中我有提到, Angular 团队是一群不爱创新.爱 follow 标准.爱小题大做的一群人. 所以,要理解 Angular Component,我们就非得要先搞懂远古 ...

  2. webpack 5.88.2

    原理 webpack的运行过程大致可以分为以下几个步骤:webpack的运行过程实际上就是等待上一个钩子结束调用下一个钩子的过程 初始化:webpack接收命令行参数或配置文件,创建一个Compile ...

  3. 记录一次BOOST库相关的使用包含互斥量、条件变量的类,引发的编译报错

    1. 工作中的代码: 2. 使用指针作为形参,不会造成编译报错,我是可以理解的. 那么请讨论下为什么使用值传递和引用作为形参,会造成编译报错? 3. 答案揭晓 boost 的mutex源码: 最终原因 ...

  4. Python实现多维傅里叶变换

    技术背景 在前面一篇文章中,我们介绍了一维离散傅里叶变换和快速傅里叶变换的基本原理和简单的代码实现.本文补充一个多维傅里叶变换的场景,以及简单的Python实现. 二维傅里叶变换 首先回顾一下上一篇文 ...

  5. 11-02 NOIP练习赛

    11-02 NOIP练习赛 为什么休息的天还要打练习赛,这不公平!!!!!!!!!! oh no! 但是三道题确实挺简单,也少见的很有意思. [USACO23OPEN] Milk Sum S 题面翻译 ...

  6. linux内核调试痛点之函数参数抓捕记

    1.linux内核调试工具crash并不能直接显示函数参数,而这个对调试又非常重要 下面是工作中一个实际的问题,我们的进程hang在如下一个内核栈中了,通过栈回溯可知是打开了一个nfs3的网盘文件或者 ...

  7. USB Type-C Power Role

    USB Power Role 是指 USB 设备在供电方面所扮演的角色,主要分为供电方(Provider)和受电方(Consumer).在 USB 供电协议中,电源角色的管理尤为重要,尤其是在 USB ...

  8. computed 中的属性名和data的属性名可以相同吗?

    不可以,因为无论computed 中的属性名还是 data 又或者是props中的属性名,都会别挂载到组件实例上,所以名字都不允许重复  : ps:好比一个作用域里面不允许定义2个相同的变量名 :

  9. Effective C++:以const、enum和inline来替换define

    替换define,也就是"以编译器替换预处理器".#define是C语言里的利器,但在C++里有很多取代它的理由. define是预处理的一部分,而不是编译器的一部分,它在编译前就 ...

  10. Oracle的用户如何优雅地达成软件合规目标

    企业一旦发展到了一定规模,就会衍生软件100%合规正版化的需求. 而对于使用到Oracle的用户,当然,具体核定的购买数量和off等商务问题,需要客户管理层直接和对应的Oracle销售代表进行商务谈判 ...