【问题标题】:SQLDataReader fails to throw exceptionSQLDataReader 无法抛出异常
【发布时间】:2017-07-10 11:29:00
【问题描述】:

下面的函数旨在确定查询是否会返回任何行。传入的 SQL 是查询。如果出现错误,该函数应返回 false。但是当 SQL =

SELECT TOP 1 [AU_ID] 
       FROM [dat].[model_80av2_v1_2941] 
       WHERE [AU_ID] IS NOT NULL AND convert(int, [AU_ID]) <> [AU_ID]

该函数错误地返回 true,因为没有检测到错误。但是在 SQL Management Studio 中执行相同的查询会导致错误:

消息 232,第 16 级,状态 3,第 3 行 int 类型的算术溢出错误,值 = -1000000000000000000000000000000.000000。

很明显,该函数应该返回 false,因为值超出了 int 数据范围,但错误处理未检测到错误。为什么?从其他帖子我的理解是SqlDataReader reader = cmd.ExecuteReader() 应该会导致错误。

private bool GetIfExists(string SQL, out int ErrorNumber, out bool Exists)
{
    bool IsSuccess = true;
    ErrorNumber = 0;
    Exists = false;

    try
    {
        using (SqlConnection cnn = new SqlConnection(_connectionString))
        {
            try
            {
                cnn.Open();

                using (SqlCommand cmd = cnn.CreateCommand())
                {
                    cmd.CommandText = SQL;
                    cmd.CommandTimeout = _commandTimeout;
                    try
                    {
                        using (SqlDataReader reader = cmd.ExecuteReader())
                        {
                            Exists = reader.HasRows;
                        }
                    }
                    catch (SqlException ex)
                    {
                        if (ex.Errors.Count > 0) ErrorNumber = ex.Errors[0].Number;
                        throw;
                    }
                    catch
                    {
                        throw;
                    }
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                cnn.Close();
            }
        }
    }
    catch
    {
        IsSuccess = false;
    }
    return IsSuccess;
}

【问题讨论】:

  • 为什么这么多接球?此外,using 语句的重点是您不必自己调用诸如 SqlConnection.Close() 之类的东西,您正在明确地这样做
  • @mjwillis sql语句在问题中
  • @CamiloTerevinto 连接池问题的纯粹恐惧。它可能过度了。
  • 附带说明,您不需要 cnn.Close(); - using 会为您处理这些问题。
  • @Peter,我认为在Exists = reader.HasRows; 之后添加reader.NextResult(); 会引发错误(dbdelta.com/the-curious-case-of-undetected-sql-exceptions)。告诉我,我会详细回答。

标签: c# .net sql-server sqldatareader


【解决方案1】:

从其他帖子我的理解是 SqlDataReader 阅读器 = cmd.ExecuteReader() 应该会导致错误。

这通常是正确的,除非在将行返回给客户端的 SQL 批处理期间发生错误。在使用完所有结果之前,可能不会引发异常。这是因为 SQL Server 通过表格数据流协议 (TDS) 将结果流式传输回客户端,并且结果集行先于 SQL Server 返回的异常。直到所有前面的结果都用完后,客户端 API 才会看到并引发错误。

以下是您可以使用和丢弃剩余结果以确保引发异常的一种方法。

using (SqlDataReader reader = cmd.ExecuteReader())
{
    Exists = reader.HasRows;
    do
    {
        while (reader.Read()) { };
    } while (reader.NextResult());
}

This article 包括 SQL 异常可能未按预期引发的其他情况。我将补充一点,您将看到 SQL Server Management Studio 使用相同查询引发的错误,因为它将FireInfoMessageEventOnUserErrors SqlConnection 属性设置为 true,而不是依赖于引发的 SqlException,这在应用程序代码中通常不会执行(也不应该它是)。

【讨论】:

  • 感谢您的简洁回答
【解决方案2】:

SQL Management Studio 可能会将 ARITH ABORT 默认设置为开启。 C# 没有。

https://dba.stackexchange.com/questions/2500/make-sqlclient-default-to-arithabort-onHow to SET ARITHABORT ON for connections in Linq To SQL 进行测验。

此外,您的以下查询是有问题的,因为 TOP 1 会根据使用的顺序给出不同的结果。要给出可重复的结果,您确实需要一个 ORDER BY 子句。如果没有它,查询优化器可以例如决定为某些查询(例如来自 C#)按列 A 对数据进行排序,对其他查询(例如来自 SQL Management Studio)按列 B 对数据进行排序。

SELECT TOP 1 [AU_ID] 
       FROM [dat].[model_80av2_v1_2941] 
       WHERE [AU_ID] IS NOT NULL AND convert(int, [AU_ID]) <> [AU_ID]

【讨论】:

  • @mjwillis 虽然很可能是这样,但字段或列实际上确实包含非整数值以及疯狂的 -1E30 值。然而没有错误并且read.HasRows当然必须返回false?好吧,确实导致了错误,它根本没有传递,并且错误陷阱无法检测到任何问题。
  • 在这种情况下IsSuccess(在返回线上)的值是多少?对或错? Exists 的值是多少?
  • @mjwillis IsSuccess = true。这是主要问题。没有检测到错误,然后暗示查询的字段中的所有行都是整数,但它们不是。
  • @mjwillis 那是对伤害的侮辱,Exists = false。
  • 它肯定不会进入catch (SqlException ex) 块?你是怎么添加ORDER BY的?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-02
  • 1970-01-01
  • 1970-01-01
  • 2013-05-24
  • 2020-08-18
  • 2011-04-05
  • 2015-09-23
相关资源
最近更新 更多