C# SqlDataReader.Read和大型记录
当使用SqlDataReader时,我会有一些奇怪的行为。当结果集包含一个大列时读取 发生的情况是: 不读取整个记录 读卡器和连接关闭后,记录保持锁定状态。 除非我在大列上显式调用reader.Get* 这里有一些测试代码来说明我的意思C# SqlDataReader.Read和大型记录,c#,sql-server-2012,sqldatareader,C#,Sql Server 2012,Sqldatareader,当使用SqlDataReader时,我会有一些奇怪的行为。当结果集包含一个大列时读取 发生的情况是: 不读取整个记录 读卡器和连接关闭后,记录保持锁定状态。 除非我在大列上显式调用reader.Get* 这里有一些测试代码来说明我的意思 CREATE TABLE TestXml ( Id int NOT NULL ,number int NOT NULL ,data xml NOT NULL ); INSERT INTO TestXml VALUES (1, 0, (SEL
CREATE TABLE TestXml (
Id int NOT NULL
,number int NOT NULL
,data xml NOT NULL
);
INSERT INTO TestXml
VALUES (1, 0, (SELECT TOP 100 * FROM sys.objects FOR XML PATH('Objects')))
,(2, 0, (SELECT TOP 1000 * FROM sys.objects FOR XML PATH('Objects')))
,(3, 0, (SELECT TOP 10000 a.* FROM sys.objects a, sys.objects b FOR XML PATH('Objects')))
获取数据的代码
const int TestId = 1;
using (var connection1 = new SqlConnection(ConnectionString))
using (var connection2 = new SqlConnection(ConnectionString))
using (var command1 = new SqlCommand())
using (var command2 = new SqlCommand()) {
command1.Connection = connection1;
command1.CommandText =
"SELECT Id, number, data " +
"FROM TestXml " +
"WHERE Id = @Id";
command2.Connection = connection2;
command2.CommandText =
"UPDATE TestXml " +
"SET number = number + 1 " +
"WHERE Id = @Id";
connection1.Open();
connection2.Open();
command1.Parameters.Add("@Id", SqlDbType.Int).Value = TestId;
using (var reader = command1.ExecuteReader()) {
if (reader.Read()) {
var id = reader.GetInt32(0);
//// reader.GetValue(2);
command2.Parameters.Add("@Id", SqlDbType.Int).Value = id;
command2.ExecuteNonQuery();
}
}
}
当Id=1或2=>时,更新工作正常。
当Id=3=>超时异常时
如果我取消对reader.GetValue2的注释,Id=3也可以正常工作
这是读者的预期行为吗?
关闭读卡器不应该取消查询并释放锁吗
我知道我可以使用CommandBehavior.SequentialAccess对结果进行流式处理,但在这种情况下,没有计划使用大型对象
编辑:只是为了表明在读卡器关闭后不需要更新就可以保留锁。任何异常都会关闭读卡器,但会保持对记录的锁定,例如
using (var connection1 = new SqlConnection(ConnectionString))
using (var command1 = new SqlCommand())
command1.Connection = connection1;
command1.CommandText =
"SELECT Id, number, data " +
"FROM TestXml " +
"WHERE Id = 3";
connection1.Open();
using (var reader = command1.ExecuteReader()) {
if (reader.Read()) {
var id = reader.GetInt32(0);
throw new Exception("whatever");
}
}
}
第3行仍然被锁定两个建议1将命令超时设置为更大的数字。默认值为5分钟,可能不够长,无法完成查询。2将查询放入BackGroundWorker,这样应用程序就不会被锁定。当我在表单应用程序中有一个大型SQL查询时,表单会挂起,因此将查询放入BackGroundWorker可以使表单在查询运行时仍能运行。我认为,在不同连接中访问同一个表的方式可能会遇到锁定问题等;这就是你其他问题的原因。很难说,因为这种方法似乎很奇怪;从一个表中运行select以在另一个表中再次更新它,而这可以在一个查询中完成—但幕后可能有比这更多的内容?@jdweng。这只是一个示例,说明reader.Read可能不会读取整个记录,并且在关闭reader时锁可能不会被清除。我的代码非常不同,但有相同的问题。对于非常大的数据库,我一直使用sqlcmd.exe并将结果保存到csv文件中。我有使用backgroundworker从c运行sqlcmd.exe的代码。然后我将csv读入c来完成处理。@jdweng,我的数据库相当小。意外地,一个大的xml被插入到一个表中,这导致了各种各样的麻烦。我四处查看了一下,但没有发现任何关于在可能包含较大值的表上使用SqlDataReader的警告。当结果集中有varcharmax、varbinarymax或xml列时,每个人都会立即切换到SequentialAccess吗?否则,这似乎是一个可能的DOS攻击的机会。有两个建议1将命令超时设置为一个更大的数字。默认值为5分钟,可能不够长,无法完成查询。2将查询放入BackGroundWorker,这样应用程序就不会被锁定。当我在表单应用程序中有一个大型SQL查询时,表单会挂起,因此将查询放入BackGroundWorker可以使表单在查询运行时仍能运行。我认为,在不同连接中访问同一个表的方式可能会遇到锁定问题等;这就是你其他问题的原因。很难说,因为这种方法似乎很奇怪;从一个表中运行select以在另一个表中再次更新它,而这可以在一个查询中完成—但幕后可能有比这更多的内容?@jdweng。这只是一个示例,说明reader.Read可能不会读取整个记录,并且在关闭reader时锁可能不会被清除。我的代码非常不同,但有相同的问题。对于非常大的数据库,我一直使用sqlcmd.exe并将结果保存到csv文件中。我有使用backgroundworker从c运行sqlcmd.exe的代码。然后我将csv读入c来完成处理。@jdweng,我的数据库相当小。意外地,一个大的xml被插入到一个表中,这导致了各种各样的麻烦。我四处查看了一下,但没有发现任何关于在可能包含较大值的表上使用SqlDataReader的警告。当结果集中有varcharmax、varbinarymax或xml列时,每个人都会立即切换到SequentialAccess吗?否则,这似乎是一个可能的拒绝服务攻击的机会。