gpt4 book ai didi

c# - while on IDataReader.Read 不适用于 yield return 但 foreach on reader 可以

转载 作者:行者123 更新时间:2023-11-29 01:38:50 26 4
gpt4 key购买 nike

这是一种常见的 ADO.NET 模式,用于使用数据读取器从数据库中检索数据,但奇怪的是它不起作用。

不起作用:

public static IEnumerable<IDataRecord> SelectDataRecord<T>(string query, string connString)
where T : IDbConnection, new()
{
using (var conn = new T())
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = query;
cmd.Connection.ConnectionString = connString;

cmd.Connection.Open();
using (var reader = (DbDataReader)cmd.ExecuteReader())
{
// the main part
while (reader.Read())
{
yield return (IDataRecord)reader;
}
}
}
}

这确实有效:

public static IEnumerable<IDataRecord> SelectDataRecord<T>(string query, string connString)
where T : IDbConnection, new()
{
using (var conn = new T())
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = query;
cmd.Connection.ConnectionString = connString;

cmd.Connection.Open();
using (var reader = (DbDataReader)cmd.ExecuteReader())
{
// the main part
foreach (var item in reader.Cast<IDataRecord>())
{
yield return item;
}
}
}
}

我看到的唯一相关变化是,在第一个代码中,迭代器是从 while 循环返回的,而在第二个代码中,它是从 foreach 循环返回的。

我这样调用它:

// I have to buffer for some reason
var result = SelectDataRecord<SQLiteConnection>(query, connString).ToList();

foreach(var item in result)
{
item.GetValue(0); // explosion
}

我试过 SQLite .NET 连接器以及 MySQL连接器。结果是一样的,即第一种方法失败,第二种方法成功。

异常

SQLite

An unhandled exception of type 'System.InvalidOperationException' occurred in System.Data.SQLite.dll. Additional information: No current row

MySQL

An unhandled exception of type 'System.Exception' occurred in MySql.Data.dll. Additional information: No current query in data reader

是不是因为reader.Readreader.GetEnumerator在特定的ADO.NET connectors中的实现差异?当我检查 System.Data.SQLite 项目的源代码时,我看不到任何明显的区别,GetEnumerator 在内部调用了 Read。我理想地假设在这两种情况下,yield 关键字可防止急切执行该方法,并且只有在外部枚举枚举器后才必须执行循环。


更新:

我使用这种模式是为了安全起见(与第二种方法基本相同,但不那么冗长),

using (var reader = cmd.ExecuteReader())
foreach (IDataRecord record in reader as IEnumerable)
yield return record;

最佳答案

这不是 while对比foreach这很重要。这是对 .Cast<T>() 的调用.

在第一个示例中,您在 while 循环的每次迭代中都对相同 对象进行了让步。如果您不小心,您最终会在实际使用数据之前完成 yield 迭代器,并且 DataReader 将被释放。如果您调用 .ToList(),就会发生这种情况。调用此方法后。您所能期望的最好结果是列表中的每条记录都具有相同的值。
(专业提示:大多数时候您不想调用 .ToList(),除非绝对必要。最好只使用 IEnumerable 记录)。

在第二个示例中,当您调用 .Cast<T>() 时在数据读取器上,当数据遍历每条记录时,您实际上是在复制数据。现在您不再生成相同的对象。

关于c# - while on IDataReader.Read 不适用于 yield return 但 foreach on reader 可以,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31747123/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com