【问题标题】:SqlCommand.ExecuteReader() does not throw SqlException when deadlockedSqlCommand.ExecuteReader() 死锁时不抛出 SqlException
【发布时间】:2015-11-23 14:22:17
【问题描述】:

我们正在使用 C# SqlCommand.ExecuteReader() 在事务中发出 SQL Server 存储过程和 SQL 请求。

当连接被选为死锁牺牲品时,ExecuteReader() 不会为某些命令抛出带有 1205 死锁代码的 SqlException,但对于其他命令则抛出。

根据MSDN

如果事务死锁,则在调用 Read 之前可能不会抛出异常。

考虑到我们使用SqlCommand对象封装在自己的数据库请求框架中,有没有办法在死锁发生时始终保证抛出异常?

我们正在使用 .Net 4.5、SQL Server 2008 R2、Visual Studio 2012

这是我们的数据库访问框架代码的简化版本:

SqlDataReader DoWork( string sql ) {
    ...
    cmd = new SqlCommand( sql );
    SqlDataReader rdr = null;

    try {
        rdr = cmd.ExecuteReader( CommandBehavior.Default ); 
    } catch (SqlException sqle) {
        // Log the error, throw a custom exception, etc.
        // if (sqle.ErrorCode == 1205) ...
        ...
        if (rdr != null) {
            rdr.Close();
            rdr = null;
        }
    }
    // All is well, so just return to caller to consume the result set
    return rdr;
}

...

main() {
    ...
    SqlDataReader result = DoWork( "select ...";

    if (result.HasRows) { // Check there is data to read...
        while (result.Read()) {
            ...
        }
    result.Close();
    ...
}

【问题讨论】:

  • 您能否出示您的代码以便我们提供帮助
  • 或者,选项 2 -- 摆脱死锁。
  • 在此问题中尝试答案stackoverflow.com/questions/4146171/…
  • @Hogan 这是这个死锁的答案,但有时会出现其他死锁。在您能够解决新死锁情况的根本原因之前,这是没有帮助的。
  • @Hogan 或者更进一步,为什么在任何代码中都有任何错误处理?为什么不直接写防错代码呢?

标签: c# .net sql-server


【解决方案1】:

这是来自该链接的代码,堆栈不允许它作为答案

function DoWork() {
using (TransactionScope scope = new TransactionScope(...)) {
cmd = new SqlCommand("select ...");
using (DataReader rdr = cmd.ExecuteReader ()) {
    while(rdr.Read()) {
      ... process each record
    }
 }
  scope.Complete ();
 }
}

【讨论】:

  • 我们遵循 MS 推荐的模式,首先使用 Read() 检查数据读取器 之前 是否有可用的行。当然,如果有任何缺点或与性能相关的问题,我们可以完全消除 HasRows。 (请参阅更新问题中的示例伪代码)
  • @alexg Read() 如果没有更多行要读取,则返回 false。因此,如果没有要读取的行,while (rdr.Read()) 将不会迭代。
【解决方案2】:

我不知道你为什么这样做:

if (result.HasRows)

这不是必需的,它可以防止出现死锁:

如果事务死锁,则在调用 Read 之前可能不会引发异常。

删除那个if。这是一种常见的反模式。它通常是由复制示例代码但没有真正理解其作用的人引入的。

这在你的catch 也是反模式:

    if (rdr != null) {
        rdr.Close();
        rdr = null;
    }

只需使用using

【讨论】:

  • 发布的代码只是我们框架中的过度简化版本。 using 不是一个选项,因为我们传递 SqlDataReader 对象。一个更基本的问题是关于 HasRows 的实用性与完全省略它。在不理解的情况下复制粘贴示例代码,这里不完全是这样……
  • 好的,也许这个答案措辞过于严格。不过,我会保持这种状态。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-13
  • 1970-01-01
  • 1970-01-01
  • 2015-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多