一起谈.NET技术,在MVC2.0 中 遭遇无法被 Try Catch 的 “Exception”
2011-09-02 00:20
375 查看
前天当我为新项目新增完日志模块后对日志模块进行测试,测试时居然发现开发人员一段非常简单的代码,而且很标准的try ... catch .. 写法。代码整理如下:
为方便大家阅读,我用 NORTHWIND 数据库。同时在该数据库内执行 SQL :update orders set EmployeeID =null where OrderID =10248 。这样造成上述代码第18 行Linq代码迭代时产生异常。您认为 这个异常可以被catch住么?答案当然是否定的!
当发现这个Bug后顿时让我产生了兴趣,我不知道如果微软的MVC项目开发人员看到这个bug,他是如何解释的。接下来让我就来一个非官方解释吧,欢迎拍砖!
我们可以在第18行设置好断点,然后用VS2010 Debug程序,发现执行到第 21 行 return Json 时 VS未报任何bug。过数秒后断点直接定位在第18行,提示也很简单:未将对象应用到实例。这个确实是我们预期想要的 Exception 但是它始终没有被 catch 住!通过StackTrace我发现了这么一句提示: at System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) 。上述代码第21行return Json 被执行后返回了 JsonAction 这个 JsonAction 被 InvokeActionResult 调用?难道这个就是问题的关键所在?原因是写有try catch 函数(本例的SaveTest()函数)的外层调用函数(InvokeActionResult()函数)出现了异常。也就是外层函数出现异常我们的内层函数SaveTest()又怎能try catch 到这个外层异常呢?于是通过Reflector进一步证实我的想法。具体的请看下图:
主要是这句action.ExecuteResult(ControllerContext);它的执行过程如下:
最后一幅图的JavaScriptSerializer serializer = new JavaScriptSerializer(); 进行 json 序列化时这个外层函数报出了异常。这个异常在我们的内层函数 SaveTest() 方法内是不可能被截获到的。此刻我已完全弄明白这个无法被catch 的exception 是怎么出现的!至于这个异常为什么会被外层函数触发,主要是.net Linq 延迟加载机制导致的,这不属于本文讨论的范畴,具体的园子内很多文章已经解释的很清楚了。
为消灭掉这个截获不到的exception现附上解决方案:
MVC虽然很年轻,但是这个架构真的很优秀我们可以非常方便对其扩展(我不是MVC的托,呵呵)。为了解决问题,我们只要自定义一个特性该特性继承 HandleErrorAttribute 特性即可。接着重写基类的 OnException 虚方法。代码如下:
接着我们在MVC Controller 内这样调用:
代码看上去更简洁了吧?连try catch 都不用写了更不用担心啥时候这个截获不到的exception神秘人物登场,这样噩梦结束了。
本例代码这里下载
public JsonResult SaveTest() { try { //LinqToSql:返回IQueryable数据集合。 var iQueryableData = (from o in _Context.Orders//.Where(o => o.OrderID == 10248) select new { ShipName = o.ShipName, Employee = o.Employee, }).ToList(); //LINQ:返回IEnumerable集合。 var iEnumerableData = from d in iQueryableData select new { ShipName = d.ShipName, EmployeeName = d.Employee.LastName //空引用未处理引发程序异常。 }; return Json(new { Success = true, Msg = iEnumerableData }, JsonRequestBehavior.AllowGet); } catch (Exception ex) { return Json(new { Success = false, Msg = ex.Message }, JsonRequestBehavior.AllowGet); } }
为方便大家阅读,我用 NORTHWIND 数据库。同时在该数据库内执行 SQL :update orders set EmployeeID =null where OrderID =10248 。这样造成上述代码第18 行Linq代码迭代时产生异常。您认为 这个异常可以被catch住么?答案当然是否定的!
当发现这个Bug后顿时让我产生了兴趣,我不知道如果微软的MVC项目开发人员看到这个bug,他是如何解释的。接下来让我就来一个非官方解释吧,欢迎拍砖!
我们可以在第18行设置好断点,然后用VS2010 Debug程序,发现执行到第 21 行 return Json 时 VS未报任何bug。过数秒后断点直接定位在第18行,提示也很简单:未将对象应用到实例。这个确实是我们预期想要的 Exception 但是它始终没有被 catch 住!通过StackTrace我发现了这么一句提示: at System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) 。上述代码第21行return Json 被执行后返回了 JsonAction 这个 JsonAction 被 InvokeActionResult 调用?难道这个就是问题的关键所在?原因是写有try catch 函数(本例的SaveTest()函数)的外层调用函数(InvokeActionResult()函数)出现了异常。也就是外层函数出现异常我们的内层函数SaveTest()又怎能try catch 到这个外层异常呢?于是通过Reflector进一步证实我的想法。具体的请看下图:
主要是这句action.ExecuteResult(ControllerContext);它的执行过程如下:
最后一幅图的JavaScriptSerializer serializer = new JavaScriptSerializer(); 进行 json 序列化时这个外层函数报出了异常。这个异常在我们的内层函数 SaveTest() 方法内是不可能被截获到的。此刻我已完全弄明白这个无法被catch 的exception 是怎么出现的!至于这个异常为什么会被外层函数触发,主要是.net Linq 延迟加载机制导致的,这不属于本文讨论的范畴,具体的园子内很多文章已经解释的很清楚了。
为消灭掉这个截获不到的exception现附上解决方案:
MVC虽然很年轻,但是这个架构真的很优秀我们可以非常方便对其扩展(我不是MVC的托,呵呵)。为了解决问题,我们只要自定义一个特性该特性继承 HandleErrorAttribute 特性即可。接着重写基类的 OnException 虚方法。代码如下:
public class CustomHandleErrorAttribute : HandleErrorAttribute { public override void OnException(ExceptionContext Context) { base.OnException(Context); dynamic ex = Context.Exception; if (!Context.ExceptionHandled) return; //TODO:将 ex 错误对象记录到系统日志模块 } }
接着我们在MVC Controller 内这样调用:
/* *截获InvokeActionResult 调用 actionResult参数的 ExecuteResult 方法。 ExecuteResult 方法执行时 进行 以下操作: JavaScriptSerializer serializer = new JavaScriptSerializer(); response.Write(serializer.Serialize(this.Data)); 将C# 匿名对象序列化成json数据供 jquery ajax 方法回调。 */ [CustomHandleError] public JsonResult SaveTest() { //try //{ //TestMehod(); //LinqToSql:返回IQueryable数据集合。 var iQueryableData = (from o in _Context.Orders//.Where(o => o.OrderID == 10248) select new { ShipName = o.ShipName, Employee = o.Employee, }).ToList(); //LINQ:返回IEnumerable集合。 var iEnumerableData = from d in iQueryableData select new { ShipName = d.ShipName, EmployeeName = d.Employee.LastName //空引用未处理引发不可截获的异常。 }; return Json(new { Success = true, Msg = iEnumerableData }, JsonRequestBehavior.AllowGet); //Json序列化。 //} //catch //外层错误,导致内层函数catch失效,无法有效的截取错误信息。 //{ // return Json(new { Success = false, Msg = "操作失败。" }, JsonRequestBehavior.AllowGet); //} }
代码看上去更简洁了吧?连try catch 都不用写了更不用担心啥时候这个截获不到的exception神秘人物登场,这样噩梦结束了。
本例代码这里下载
相关文章推荐
- 在MVC2.0 中 遭遇无法被 Try Catch 的 “Exception”
- 一起谈.NET技术,.Net中Exception你这样做了么?
- 一起谈.NET技术,MVC2.0本地化(另类解决方案)<上>
- 一起谈.NET技术,在MVC2.0使用Lodop为WEB打印提出完美解决方案
- 一起谈.NET技术,编写T4模板无法避免的两个话题:"Assembly Locking"&"Debug"
- 一起谈.NET技术,在.NET中嵌入IronPython 交互
- 一起谈.NET技术,NGuestBook架构体系及实现指南
- 一起谈.NET技术,NHibernate3.0剖析:Query篇之NHibernate.Linq标准查询
- 一起谈.NET技术,如何实现ASP.NET网站个性化?
- 一起谈.NET技术,JavaScript 调用 ASP.NET WebService 的简单方法
- 一起谈.NET技术,现有ASP.NET MVC框架应用巧妙移植到手机
- 一起谈.NET技术,asp.net页面中输出变量、Eval数据绑定等总结
- 一起谈.NET技术,.Net线程问题解答
- 一起谈.NET技术,Silverlight 4.0发布之际,让我们重新再认识一次Silverlight这位乱世英雄!
- 一起谈.NET技术,浅析购物车的实现
- 一起谈.NET技术,DataReader 程序性能优化
- 一起谈.NET技术,VS 2010 和 .NET 4.0 系列之《在VS 2010中查询和导航代码》篇
- 一起谈.NET技术,ASP.NET 4过滤数据新控件QueryExtender
- 一起谈.NET技术,ASP.NET 请求处理流程
- 一起谈.NET技术,如何解决分布式系统中的跨时区问题[原理篇]