我想知道迭代数据读取器对象时’yield’的连接状态和对代码性能的影响
这是我用来从数据库中获取数据的示例代码:在DAO层:
public IEnumerable GetDATA(ICommonSearchCriteriaDto commonSearchCriteriaDto) { using(DbContext) { DbDataReader reader = DbContext.GetReader("ABC_PACKAGE.GET_DATA", oracleParams.ToArray(), CommandType.StoredProcedure); while (reader.Read()) { yield return reader; } } }
在BO层我调用上面的方法,如:
List GridDataDtos = MapMultiple(_costDriversGraphDao.GetGraphData(commonSearchCriteriaDto)).ToList();
在mapper层上MapMultiple方法的定义如下:
public IGridDataDto MapSingle(IDataRecord dataRecord) { return new GridDataDto { Code = Convert.ToString(dataRecord["Code"]), Name = Convert.ToString(dataRecord["Name"]), Type = Convert.ToString(dataRecord["Type"]) }; } public IEnumerable MapMultiple(IEnumerable dataRecords) { return dataRecords.Select(MapSingle); }
上面的代码运行良好,但我想知道上述代码的两个问题。
- 数据阅读器的连接将打开多长时间?
- 当我只考虑代码性能因素时,使用’yield return’而不是将记录添加到列表并返回整个列表是一个好主意吗?
- 您的代码未显示您打开/关闭连接的位置; 但是这里的读者实际上只有在你迭代数据时才会打开。 延迟执行等。执行此操作的代码的唯一位是
.ToList()
,因此它会没问题。 在更一般的情况下,是的:读者将花费你花费的时间来迭代它; 如果你做一个最小的.ToList()
; 如果你做一个foreach
并且(对于每个项目)发出外部http请求并等待20秒,那么是 – 它会打开更长时间。 - 两者都有用途; 非缓冲方法非常适合您想要作为流处理的巨大结果,而无需将它们加载到单个内存列表中(或者甚至一次将所有内容加载到内存中); 返回列表可以快速关闭连接,并且在已经有一个打开的阅读器时可以很容易地避免意外使用连接,但对于大结果却不是理想的
如果你返回一个迭代器块,调用者可以决定什么是理智的; 如果你总是返回一个列表,他们没有太多选择。 第三种方式(我们在短小精悍的做法)是做出他们的选择; 我们有一个可选的bool
参数,默认为“返回一个列表”,但调用者可以更改它以指示“返回迭代器块”; 基本上:
bool buffered = true
在参数中,和:
var data = QueryInternal(...blah...); return buffered ? data.ToList() : data;
在实施中。 在大多数情况下,返回列表是完全合理的,并避免了很多问题,因此我们将其作为默认值。
数据阅读器的连接将打开多长时间?
连接将保持打开状态,直到reader
被解除,这意味着它将一直打开,直到迭代结束。
当我只考虑代码性能因素时,使用
yield return
是一个好主意,而不是将记录添加到列表中并返回整个列表?
这取决于几个因素:
- 如果您不打算获取整个结果,则
yield return
将帮助您节省网络上传输的数据量 - 如果您不打算将返回的数据转换为对象,或者如果使用多行创建单个对象,则
yield return
将帮助您节省程序高峰使用点使用的内存 - 如果您计划在短时间内迭代enture结果集,则使用
yield return
不会有性能损失。 如果迭代将在多个并发线程上持续相当长的时间,则可能会超出RDBMS端上的打开游标数。
这个答案忽略了所示实现中的缺陷并涵盖了一般概念。
这是一种权衡 – 如果不知道系统的限制,就不可能知道这是一个好主意 – 你期望获得的数据量,你愿意接受的内存消耗,数据库的预期负载,等等