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 捕获异常的主要内容,如果未能解决你的问题,请参考以下文章
MVC / LINQ /合并Iqueryable对象[关闭]
在 ASP.NET MVC 中向控制器传递多个参数;此外,在 LINQ-to-SQL 中生成动态查询