.NET:SqlDataReader.Close或者.Dispose导致Timeout Expired异常
时间:2020-03-06 14:43:16 来源:igfitidea点击:
尝试在SqlDataReader上调用Close或者Dispose时,我收到超时过期的异常。如果我们具有SQL Server的DbConnection,则可以使用以下方法自己重现它:
String CRLF = "\r\n";
String sql =
"SELECT * " + CRLF +
"FROM (" + CRLF +
" SELECT (a.Number * 256) + b.Number AS Number" + CRLF +
" FROM master..spt_values a," + CRLF +
" master..spt_values b" + CRLF +
" WHERE a.Type = 'p'" + CRLF +
" AND b.Type = 'p') Numbers1" + CRLF +
" FULL OUTER JOIN (" + CRLF +
" SELECT (print("code sample");a.Number * 256) + b.Number AS Number" + CRLF +
" FROM master..spt_values a," + CRLF +
" master..spt_values b" + CRLF +
" WHERE a.Type = 'p'" + CRLF +
" AND b.Type = 'p') Numbers2" + CRLF +
" ON 1=1";
DbCommand cmd = connection.CreateCommand();
cmd.CommandText = sql;
DbDataReader rdr = cmd.ExecuteReader();
rdr.Close();
如果调用reader.Close()或者reader.Dispose(),它将抛出System.Data.SqlClient.SqlException:
- 错误代码:-2146232060(0x80131904)
- 消息:"超时已到期。在操作完成之前超时时间已过,或者服务器没有响应。"
解决方案
这是因为我们刚刚打开了数据读取器,但还没有完全遍历它。我们将需要.Cancel()DbCommand对象,然后再尝试关闭尚未完成的数据读取器(以及DbConnection)。当然,通过.Cancel()-DbCommand,我不确定这一点,但是我们可能会遇到其他异常。但如果发生这种情况,我们应该抓住它。
我们实际在哪里读取数据?我们只是在创建一个阅读器,而不是在阅读数据。这只是一个猜测,但如果我们不阅读,读者可能会遇到问题;)
DbDataReader rdr = cmd.ExecuteReader();
while(rdr.Read())
{
int index = rdr.GetInt32(0);
}
Cruizer的回答是:调用command.Cancel():
using (DbCommand cmd = connection.CreateCommand())
{
cmd.CommandText = sql;
using (DbDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
if (WeShouldCancelTheOperation())
{
cmd.Cancel();
break;
}
}
}
}
知道即使读者已经读取了所有行,我们也可以调用Cancel(这也没有帮助)(即它不会抛出" nothing to cancel"异常)。
DbCommand cmd = connection.CreateCommand();
try
{
cmd.CommandText = sql;
DbDataReader rdr = cmd.ExecuteReader();
try
{
while (rdr.Read())
{
if (WeShouldCancelTheOperation())
break;
}
cmd.Cancel();
}
finally
{
rdr.Dispose();
}
}
finally
{
cmd.Dispose();
}

