我想知道迭代数据读取器对象时’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); } 

上面的代码运行良好,但我想知道上述代码的两个问题。

  1. 数据阅读器的连接将打开多长时间?
  2. 当我只考虑代码性能因素时,使用’yield return’而不​​是将记录添加到列表并返回整个列表是一个好主意吗?

  1. 您的代码未显示您打开/关闭连接的位置; 但是这里的读者实际上只有在你迭代数据时才会打开。 延迟执行等。执行此操作的代码的唯一位是.ToList() ,因此它会没问题。 在更一般的情况下,是的:读者将花费你花费的时间来迭代它; 如果你做一个最小的.ToList() ; 如果你做一个foreach并且(对于每个项目)发出外部http请求并等待20秒,那么是 – 它会打开更长时间。
  2. 两者都有用途; 非缓冲方法非常适合您想要作为流处理的巨大结果,而无需将它们加载到单个内存列表中(或者甚至一次将所有内容加载到内存中); 返回列表可以快速关闭连接,并且在已经有一个打开的阅读器时可以很容易地避免意外使用连接,但对于大结果却不是理想的

如果你返回一个迭代器块,调用者可以决定什么是理智的; 如果你总是返回一个列表,他们没有太多选择。 第三种方式(我们在短小精悍的做法)是做出他们的选择; 我们有一个可选的bool参数,默认为“返回一个列表”,但调用者可以更改它以指示“返回迭代器块”; 基本上:

 bool buffered = true 

在参数中,和:

 var data = QueryInternal(...blah...); return buffered ? data.ToList() : data; 

在实施中。 在大多数情况下,返回列表是完全合理的,并避免了很多问题,因此我们将其作为默认值。

数据阅读器的连接将打开多长时间?

连接将保持打开状态,直到reader被解除,这意味着它将一直打开,直到迭代结束。

当我只考虑代码性能因素时,使用yield return是一个好主意,而不是将记录添加到列表中并返回整个列表?

这取决于几个因素:

  • 如果您不打算获取整个结果,则yield return将帮助您节省网络上传输的数据量
  • 如果您不打算将返回的数据转换为对象,或者如果使用多行创建单个对象,则yield return将帮助您节省程序高峰使用点使用的内存
  • 如果您计划在短时间内迭代enture结果集,则使用yield return不会有性能损失。 如果迭代将在多个并发线程上持续相当长的时间,则可能会超出RDBMS端上的打开游标数。

这个答案忽略了所示实现中的缺陷并涵盖了一般概念。

这是一种权衡 – 如果不知道系统的限制,就不可能知道这是一个好主意 – 你期望获得的数据量,你愿意接受的内存消耗,数据库的预期负载,等等