SqlCommand.ExecuteReader()在发生死锁时不会抛出SqlException异常。

3
我们正在使用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();
    ...
}

能否请您展示一下您的代码,以便我们能够提供帮助? - Simon Price
3
选项2:消除僵局。 - Hogan
请尝试在此问题中查看答案:https://dev59.com/KFHTa4cB1Zd3GeqPUcj1 - Simon Price
3
@Hogan的回答适用于此死锁情况,但有时可能会出现其他情况。除非您能够解决新死锁情况的根本原因,否则这并没有什么帮助。请注意不要改变原意。 - JNK
4
为什么不写一个无错误的代码,而要在代码中添加错误处理?甚至更进一步说,为什么不写一个完全无需错误处理的代码呢? - JNK
2个回答

4

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

if (result.HasRows)

这并不必要,它可以防止死锁的出现:

如果一个事务发生死锁,直到调用Read方法时才会抛出异常。

删掉那个if。这是一种常见的反模式。通常是由复制示例代码而没有真正理解其含义的人引入的。

catch中使用this也是一种反模式:

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

只需使用 using


发布的代码只是我们框架中存在的过度简化版本。using不是选项,因为我们在对象之间传递SqlDataReader。更基本的问题是HasRows的效用与完全省略它之间的区别。这里并不是简单地复制和粘贴示例代码而没有理解。 - alexg
好的,也许这个答案措辞太严格了。不过我会保持这样的方式。 - usr

0

这是来自那个链接的代码,Stack不允许它作为答案。

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

我们一直遵循微软推荐的模式,即在使用Read()之前首先检查数据读取器是否有可用行。当然,如果存在任何缺点或性能相关问题,我们完全可以完全消除HasRows。 (请参见更新的问题中的示例伪代码) - alexg
@alexg 如果没有更多的行可读,则Read()会返回false。因此,如果没有行可读取,则while(rdr.Read())不会迭代。 - saarrrr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接