当reader关闭时,使用SqlDbReader方法调用CheckDataIsReady无效.例外
本文关键字:调用 方法 CheckDataIsReady 无效 例外 SqlDbReader 使用 reader | 更新日期: 2023-09-27 17:51:05
我用一个参数调用这个方法来调用一个存储过程,我正在使用这个存储过程来查看yield是否对我的团队有利。
这是一个调用存储过程的通用方法。对它的调用工作正常,
var parameters = new List<SqlParameter>();
parameters.Add(CreateParamter("GL45", "@prefix", SqlDbType.VarChar));
var reader = StoreProcedureReader("Yield", parameters.ToArray());
返回阅读器需要的内容,但是下一行
string value = reader.First()[0].ToString();
导致异常, reader关闭时调用CheckDataIsReady无效。
public static IEnumerable<IDataRecord> StoreProcedureReader(String StoredProcedureName, SqlParameter[] paramters)
{
using (var conn = new SqlConnection(MyConnectionString))
{
using (var cmd = new SqlCommand(StoredProcedureName, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddRange(paramters);
conn.Open();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
yield return reader;
}
}
}
}
}
我在StackOverflow上看了几个例子,似乎我可能错过了一些东西,一个例子在sqlCommand使用语句后关闭,我试过了,但把它拿了出来。我可以关闭一个,我得到了一些东西
我认为这个问题是由于使用了yield return
。在对.First()
的调用返回之前,一旦枚举数被释放,阅读器将被释放。
因此,当您在结果IDataRecord
上使用索引器[0]
时,它会抛出异常,因为返回的IDataRecord
(即SqlDataReader
)已经被处置了。
最好的方法可能是填充并复制reader的结果,这样可以确保在reader被丢弃之前检索到结果,即
public static IEnumerable<IList<object>> StoreProcedureReader(String StoredProcedureName, SqlParameter[] paramters)
{
using (var conn = new SqlConnection(MyConnectionString))
{
using (var cmd = new SqlCommand(StoredProcedureName, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddRange(paramters);
conn.Open();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var fieldCount = reader.FieldCount;
var results = new object[fieldCount];
for(var i = 0; i < fieldCount; ++i)
{
results[i] = reader[i];
}
yield return results;
}
}
}
}
}
注意IEnumerable<IList<object>>
的返回类型,你的调用代码不需要改变。
这样做的缺点是所有的数据都被读入内存。另一种避免这种情况的方法是手动控制生成的IEnumerator<T>
,因此使用原始代码,执行以下操作:
using(var enumerator = reader.GetEnumerator())
{
// Move to first
if(!enumerator.MoveNext())
throw new NotImplementedException();
var value = enumerator.Current[0].ToString();
}
这将确保在读取感兴趣的值之前不会处理生成的IEnumerator<T>
。