zoukankan      html  css  js  c++  java
  • 一起谈.NET技术,在MVC2.0 中 遭遇无法被 Try Catch 的 “Exception” 狼人:

      前天当我为新项目新增完日志模块后对日志模块进行测试,测试时居然发现开发人员一段非常简单的代码,而且很标准的try ... catch .. 写法。代码整理如下:

    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神秘人物登场,这样噩梦结束了。

      本例代码这里下载

  • 相关阅读:
    vue form dynamic validator All In one
    TypeScript api response interface All In One
    closable VS closeable All In One
    macOS 如何开启 WiFi 热点 All In One
    vue css inline style All In One
    vs2010里面 新建网站里面的 asp.net网站 和 新建项目里面的 asp.net Web应用程序 的区别 (下)
    牛腩新闻 59 整合添加新闻页 FreeTextBox 富文本编辑器,检测到有潜在危险的 Request.Form 值,DropDownList 的使用
    牛腩新闻 61尾声: error.aspx的使用 防止报错
    vs2010里面 新建网站里面的 asp.net网站 和 新建项目里面的 asp.net Web应用程序 的区别 (上)
    牛腩新闻 62:尾声续2 asp.net的编译和发布
  • 原文地址:https://www.cnblogs.com/waw/p/2163172.html
Copyright © 2011-2022 走看看