[转载]使用IntelliTrace调试跟踪ASP.NET MVC框架Action调用

[转载]使用IntelliTrace调试跟踪MVC框架Action调用 – lipan – 博客园.

IntelliTrace调试跟普通断点加单步跟踪模式的区别在于,它支持对历史过程的模拟重新调试。当我们在普通调试下想了解应用程序曾经的 执行情况,一般情况下我们会停止调试,重新加断点启动调试。而有了IntelliTrace之后,我们可以用其独有的历史调试功能“回到过去”,这样一次 调试就可以有效定位问题。现在我要用这个功能,在开源MVC框架中寻找控制器的Action方法是如何被调用的。

大家都知道,MVC通过URL路由截获地址栏参数获取Controller和Action的值,并通过这两个两个字符串型的去定位控制器和控制 器的方法,再由这个方法返回视图。可问题在于,只知道字符串的类名和方法名是没有办法直接实现类并调用方法的。于是“很自然的”就想到了反射。由于反射的 性能代价太大,很多人就开始抱怨,微软的新特性都是以牺牲性能为代价的,C#是性能低下的语言。然而事实是什么样呢,话说没有调查就没有发言权,我们先展 开调查。

将MVC开源文件引入项目

1. 下载MVC框架源码: ASP.NET MVC 2 源码 ASP.NET MVC 3 源码 ,本文用的是MVC2。

2. 在VS2010新建一个MVC项目,删除引用“System.Web.Mvc”。将源码包解压,将src下SystemWebMvc目录拷贝至项目文件夹,在解决方案中添加项目,再添加对这个开源项目的引用。

点击下载配置好的项目

使用IntelliTrace调试

第一步,由于IntelliTrace调试默认是未启用的,首先你要开启它。安F5进入调试状态,看到右边的“IntelliTrace”窗 口,单击打开IntelliTrace设置按钮。勾选“启用IntelliTrace”,单选组合点选“IntelliTrace事件和调用信息”,如下 图所示。

图1

配置好后点确定,然后停止调试,在HomeController的Index方法处加以断点,启动调试,程序请求Index页面命中断点,这时你会发现围绕断点处多了几个箭头符号,这就是IntelliTrace调试要用到的。

好了,现在我们就要展示IntelliTrace调试的神奇之处了。由于我们想知道Index方法到底被谁调用了,我们怎么操作?见证奇迹的时刻就要到了!我 们要让程序倒着执行,是不是就很容易知道它的调用者?看到有个向上的双箭头,我叫他“单步回退”,单击一次,程序定位到了某个类的Execute方法中, 它调用了名为_executor委托,然后我们分析代码发现这个委托在其下方的GetExecutor函数中被实现,我们重点关注 GetExecutor,我将其贴在下面。

01 private static ActionExecutor GetExecutor(MethodInfo methodInfo) {
02 // Parameters to executor
03 ParameterExpression controllerParameter = Expression.Parameter(typeof(ControllerBase), "controller");
04 ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
05
06 // Build parameter list
07 List<Expression> parameters = new List<Expression>();
08 ParameterInfo[] paramInfos = methodInfo.GetParameters();
09 for (int i = 0; i < paramInfos.Length; i++) {
10 ParameterInfo paramInfo = paramInfos[i];
11 BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
12 UnaryExpression valueCast = Expression.Convert(valueObj, paramInfo.ParameterType);
13
14 // valueCast is "(Ti) parameters[i]"
15 parameters.Add(valueCast);
16 }
17
18 // Call method
19 UnaryExpression instanceCast = (!methodInfo.IsStatic) ? Expression.Convert(controllerParameter, methodInfo.ReflectedType) : null;
20 MethodCallExpression methodCall = methodCall = Expression.Call(instanceCast, methodInfo, parameters);
21
22 // methodCall is "((TController) controller) method((T0) parameters[0], (T1) parameters[1], ...)"
23 // Create function
24 if (methodCall.Type == typeof(void)) {
25 Expression<VoidActionExecutor> lambda = Expression.Lambda<VoidActionExecutor>(methodCall, controllerParameter, parametersParameter);
26 VoidActionExecutor voidExecutor = lambda.Compile();
27 return WrapVoidAction(voidExecutor);
28 }
29 else {
30 // must coerce methodCall to match ActionExecutor signature
31 UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));
32 Expression<ActionExecutor> lambda = Expression.Lambda<ActionExecutor>(castMethodCall, controllerParameter, parametersParameter);
33 return lambda.Compile();
34 }
35 }

研究过表达式树的同鞋一定知道,在这个方法中构建了一个调用Action方法的表达式树,并通过lambda.Compile()返回调用过程的委托。lambda表达式树调用方法的效率如何,老赵早已在这篇文章[点击学习]分析过,它的效率跟静态调用相差无几的。所以担心“反射”降低性能的朋友大可以放心了。

后记

本文虽然主题不够明确,讲了调试又讲MVC(我就喜欢跟着思维走,呵呵),也反映了些问题。假如我们通过一般的调试手段去分析这个问题,那么我 们得先把MVC源码给分析一边吧,然后呢,我们好容易找到了入口点,Controller的 Execute方法,加了断点了,然后就开始淌水了,趟了好深好深的一趟水,然后执行Action了,然后我们要再次调试,回忆刚才从哪里跳到 Action,终于找到了。好辛苦!而用IntelliTrace只是轻轻点击下鼠标,就完成了任务。另外,选MVC框架源码的作用,说明项目足够复杂 时,这样的调试功能的价值才能体现出来。另外,不是也有个收获么,知道了MVC框架怎么通过地址栏参数映射到方法了。

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏