ASP.NET MVC 控制器不会从 LINQ lambda 捕获异常

Posted

技术标签:

【中文标题】ASP.NET MVC 控制器不会从 LINQ lambda 捕获异常【英文标题】:ASP.NET MVC Controller won't catch exceptions from LINQ lambda 【发布时间】:2021-10-18 13:03:17 【问题描述】:

我有一个沼泽标准 MVC 控制器,在代码周围有一个 try/catch,以便在发生异常时能够返回一些自定义数据。这适用于直接在控制器中或直接在控制器使用的方法中抛出的异常。但是,如果在 LINQ lambda 中引发异常,则根本不会捕获异常,而是显示黄屏死机 (YSOD)。

public class MyController : Controller 
    public JsonResult Index() 
        try 
            var data = new TestService().GetTestData();
            return Json(data.Entries.Select(d => d.SomeExtensionMethod(), JsonRequestBehavior.AllowGet);
        
        catch (Exception e) 
            return Json("Something went wrong");
        
    

如果我在 try/catch 中手动抛出异常,则返回“出现问题”。如果在GetTestData() 中抛出异常,则返回“Something went wrong”。

如果d.SomeExtensionMethod() 抛出异常,则显示 YSOD。

为什么 LINQ lambda 中的异常没有被 try/catch 捕获,我该怎么做才能真正捕获它?

【问题讨论】:

【参考方案1】:

这不会立即评估:

data.Entries.Select(d => d.SomeExtensionMethod())

它创建了一个表达式树,可以在以后进行评估。这允许将多个操作(对.Where().OrderBy().GroupBy() 等的调用)链接到一个更大的表达式树中,以便它只针对支持数据源执行一次。

因为当支持数据源是数据库时,您不希望立即实现整个表,然后执行过滤/排序/等。在代码中,而是希望在代码中构建整个查询,然后针对高度优化的数据库引擎执行一次。

在这种情况下,这意味着表达式树,而不是结果数据,沿着框架中的管道传递,并且稍后的一些框架内部操作最终调用该表达式树来实现结果,并且 那是发生异常的时间。在这个控制器方法已经返回之后。

对于这种特殊情况,您可以放心地强制代码评估表达式树并立即具体化结果。一个简单的方法是使用.ToList():

data.Entries.Select(d => d.SomeExtensionMethod()).ToList()

在这种情况下应该没有什么不同,因为您不会对该表达式树进行任何更改,并且无论如何它都将由框架评估。

但是,请注意,这种方法并非在所有此类情况下都是一种即插即用的解决方案。需要检查这些查询的任何单个实例,以了解强制查询具体化结果可能会如何影响性能。例如,假设一个简单的.Select() 结果被传递给一个视图,但是在视图本身内,更多的操作被附加到该结果。如果控制器添加了.ToList(),那么这些操作将在整个内存集上执行,而不是针对数据库。

如果视图有类似.Take(10) 的内容,那么意图是只从数据库中获取前十条记录,但在流程的早期附加.ToList() 意味着它现在正在获取全部 数据库中的记录(数亿?数百万?更多?),然后只显示前十个。您可以查看这可能会导致性能问题的位置。

由于在显示的代码中的目的是返回所有记录,因此强制系统提前获取所有记录并没有什么坏处。

【讨论】:

感谢您的详细解释。我已经知道 LINQ 的执行计划,但我只是没想到我的代码周围会有脚手架,可以在我的方法之外执行它。当然,添加.ToList() 效果很好:)

以上是关于ASP.NET MVC 控制器不会从 LINQ lambda 捕获异常的主要内容,如果未能解决你的问题,请参考以下文章

ASP.net MVC LINQ 空引用异常 [重复]

MVC / LINQ /合并Iqueryable对象[关闭]

在 ASP.NET MVC 中向控制器传递多个参数;此外,在 LINQ-to-SQL 中生成动态查询

Asp.Net MVC 开发技巧

ASP.NET MVC4.0+EF+LINQ+bui+网站+角色权限管理系统

如何在 Asp.net MVC C# 中使用 Linq 从多个表中选择具有最大计数值的记录