Web API EF核心异常被吞没了吗?

时间:2019-09-05 12:13:05

标签: asp.net-core entity-framework-core odata

我在ASPNetCore中有一个基本的OData Web Api,用于OData查询的get方法是:

[EnableQuery]
public IQueryable Get() => _db.AllThings;

但是,如果在此调用期间遇到任何SQL异常,则该异常似乎不会在任何地方被捕获,抛出或记录。

例如如果我强制执行查询:

var q = _db.AllPOs.Take(1);
first = q.First();

然后引发以下异常:

An exception occurred while reading a database value for property 
'AllThing.Number'. The expected type was 'System.Int32' but the actual value 
was of type 'System.String'.
Source=Microsoft.EntityFrameworkCore.Relational

那么为什么在通过返回IQueryable推迟执行时却没有抛出该错误?

1 个答案:

答案 0 :(得分:3)

请注意,这并不是IQueryable所独有的,而是尚未实现结果的任何IEnumerable所独有的。

实际查询是在枚举对象时执行的,这是由ASP.NET在构造响应时完成的-在代码之外,这就是为什么您在Get()方法中看不到异常的原因

查看异常的唯一方法是调试.NET Core代码,您可以按照说明here进行操作。两个重要步骤是禁用“仅我的代码”并启用下载符号。

为什么它返回200并带有无效的JSON

这是因为它如何构造响应。它不会将查询结果缓冲到内存中,然后将其发送。它先发送响应头,然后然后枚举结果,并在它们出现时发送它们 。您可以将其称为“流式处理”结果,因为结果没有保存在内存中。

在结果可能非常大的情况下,这可能是一件好事。没有必要将整个结果集加载到内存中只是为了再次发送。

但是缺点是,如果在枚举结果时发生错误,您将无法再更改响应头(包括HTTP状态代码),因为该响应头已经到达客户端。在异常发生的那一刻,响应流将关闭,无需进一步编写任何内容。这会导致无效的JSON。

选项

您将不得不权衡流式传输结果与令人困惑的错误的好处。您有两种选择:

  1. 在全局异常处理程序中捕获异常,这样您就可以知道,但是您无法更改对客户端的200响应。

  2. 根据需要,强制它使用ToList()First()之类的方法在您的方法中实际进行查询。这样做的好处是可以捕获所有那里异常,并给出更有意义的错误消息,但确实会将所有结果首先加载到内存中。

public IEnumerable Get() {
    try {
        return _db.AllThings.ToList();
    } catch (Exception e) {
        _logger.LogError(e, "There was an exception getting "AllThings");
        throw; //this will rethrow the exception, or you can return some other IQueryable
    }
}

这是一个错误吗?

结果并不理想,但是很难找到解决方案。实际上,这确实是在之前提出的,conclusion尤其是在讲述这一变通办法时,是在讲:

  

显然,这两种情况都需要更多的服务器端资源,这就是为什么我们默认情况下不具有这种行为的原因。

更改错误时响应代码的唯一方法是在开始响应之前将所有内容缓冲到内存中。但是,如果他们默认情况下这样做了,那么可以说会不太直观-如果返回IEnumerable,则应该期望它可以流式传输响应,因为通常这实际上是IEnumerable的全部好处。

所以...¯\ _(ツ)_ /¯

相关问题