今天遇到一个问题,需要在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. Azure – 对比 AWS Research Report

    前言 最近有机会接触了一下 AWS, 在对比完之后决定继续用 Azure, 这里小小记入一下. VM & SQL Server Azure 和 AWS 都可以选择 2 in 1, 或者 Web ...

  2. 以太坊Rollup方案之 arbitrum(2)

    上一期简单介绍了一下rollup的一些基本内容以及aritrun交易的执行流程,这一期将介绍一下aritrum的核心技术 -- 交互式单步证明 这一期主要涉及到的是arbitrum的验证节点 arbi ...

  3. 这才是批量update的正确姿势!

    前言 最近我有位小伙伴问我,在实际工作中,批量更新的代码要怎么写. 这个问题挺有代表性的,今天拿出来给大家一起分享一下,希望对你会有所帮助. 1 案发现场 有一天上午,在我的知识星球群里,有位小伙伴问 ...

  4. Fio工具详解【强大的IO性能压测工具】

    Fio压测工具操作 fio -name=iouring_test -filename=/mnt/vdd/testfile -iodepth=128 -thread -rw=randread -ioen ...

  5. 数组对象删除不满足某些条件的对象 js

    recursiveFunction(items, childrenNodeName, ids) { console.log('items', ids); // 获取数组长度 if (items) it ...

  6. 妙用编辑器:使用Notepad--列编辑功能批量生成维护命令

    应用场景 在日常工作中,维护人员可能会要批量生成一些配置命令,示例如下:添加12个分组 ADD GROUP:GID=1,FCN=646322; ADD GROUP:GID=1,FCN=646322; ...

  7. Pandas 空值数据的索引 位置 行号

    前言 先说一下什么是pandas, 这个东西其实就是一个处理表格数据的一个库.可以把它看做是一个没有图形化界面的Excel. Pandas中的空值是非常多的,这体现了数据搜集的一个不可避免的方面.由于 ...

  8. 一文彻底弄懂MySQL优化之深度分页

    深度分页(Deep Pagination)在MySQL中指的是对大型数据集进行分页查询时,尤其是当需要获取较后页的数据时,性能可能会受到影响.传统的分页方法在数据量较大时,随着页数的增加,性能会迅速下 ...

  9. mongodb插入数据不能在vue显示

    问题描述:当我们在命令行插入数据时,在MongoVUE却显示不了数据,并且查询有插入数据的数据库,如下图所示 网上资料说,这是引擎的问题,mongoDB3.2版本之后默认开启的存储引擎是wiredRi ...

  10. 关于Java后台处理前端用户传来数据过大,超过数据库字段设置长度导致的异常问题处理

    在DTO bean对象上使用javax验证(如最小值,最大值等,请参阅here).