【发布时间】:2018-12-31 00:47:12
【问题描述】:
我有一个执行 SQL 语句的方法,它返回我的类 ResultSet 的实例,其中包含返回的行和列。我只想在调用者实际迭代结果时逐行读取数据。
public class ResultSet {
public IEnumerable<Row> Rows {
get;
private set;
}
public ResultSet(IEnumerable<Row> rows, IEnumerable<Column> columns) {
Rows = rows;
// columns and other code omitted
}
}
为此,我尝试将 IEnumerable 传递给我的类的构造函数,但问题是 DbDataReader 实例在您尝试迭代 Rows 属性时已经被释放ResultSet 实例:
public ResultSet Execute(string sql) {
using (var command = Connection.CreateCommand()) {
command.CommandText = sql;
var reader = command.ExecuteReader();
try {
IEnumerable<Row> MyFunc()
{
while (reader.Read())
yield return new Row(reader);
}
var columns = GetColums(...);
return new ResultSet(MyFunc(), columns);
} finally {
reader.Dispose();
}
}
}
我知道我可以将 DbDataReader 实例传递给我的班级,而不是在 Execute 方法中处理它,但是我必须将 ResultSet 设为一次性,如果可能的话,我想避免这种情况。我不确定我想要做什么?
我看过yield return statement inside a using() { } block Disposes before executing,但不完全一样,因为它不涉及一次性资源。
【问题讨论】:
-
创建一个自定义
IEnumerable,在调用GetEnumerator时调用ExecuteReader,而不是更早。 (迭代器方法也可以返回IEnumerator,所以这并不像听起来那么麻烦。)或者,总是可以选择通过这种手动ORM代码并使用Dapper,它往往适合这类场景很好。 -
或者创建一个自定义
IEnumerable<Row>构建自定义IEnumerator<Row>,在第一次调用MoveNext时执行查询。 -
读取器已经逐行读取数据(除非它是一小组记录,在这种情况下,它会在内部一次性读取它们)那么为什么需要使用
yield? (免责声明:我不喜欢yield)。 -
要么推迟整个请求(行和列),要么需要 2 个请求(一个用于急切加载列,一个用于延迟加载行)。你喜欢什么?
-
@Spotted 你说得对,现在我想起来了。我想我会选择返回 DbDataReader 实例,并为其提供 GetRows 和 GetColumns 的扩展方法。
标签: c# idisposable yield deferred-execution dbdatareader