当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使用语句后关闭,我试过了,但把它拿了出来。我可以关闭一个,我得到了一些东西

当reader关闭时,使用SqlDbReader方法调用CheckDataIsReady无效.例外

我认为这个问题是由于使用了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>