使用IEnumerable 返回类型的SQL数据是否有任何陷阱?

我的问题是关于SQL连接状态,负载等基于以下代码:

public IEnumberable GetMyTypeObjects() { string cmdTxt = "select * from MyObjectTable"; using(SqlConnection conn = new SqlConnection(connString)) { using(SqlCommand cmd = new SqlCommand(cmdTxt, conn)) { conn.Open(); using(SqlDataReader reader = cmd.ExecuteReader()) { while(reader.Read()) { yield return Mapper.MapTo(reader); } } } } yield break; } 

我可以看到这可能是一个问题,如果有许多进程在IEnumerable对象的迭代之间运行类似的代码,执行时间长,因为连接将打开更长时间等等。但是,这似乎也可能会减少CPU使用率在SQL服务器上,因为它只在使用IEnumerable对象时返回数据。 它还降低了客户端上的内存使用量,因为客户端只需在其工作时加载一个MyType实例,而不是加载所有出现的MyType(通过迭代整个DataReader并返回List或其他内容)。

  • 您是否有任何实例可以想到您不希望以这种方式使用IEnumerable,或者您认为它完全适合的任何实例?

  • 这会给SQL服务器带来什么样的负载?

  • 这是你在自己的代码中使用的东西(除非提到NHibernate,Subsonic等)吗?

我不会使用它,因为它隐藏了正在发生的事情,它可能会在没有适当处置的情况下留下数据库连接。

读取超过最后一条记录后,将关闭连接对象,如果在此之前停止读取,则不会处理连接对象。 例如,如果您知道结果中总是有十条记录,并且只有一个循环从枚举器中读取这十条记录而没有使第十一条读取调用超出最后一项,则连接未正确关闭。 此外,如果您只想使用部分结果,则无法在不读取其余记录的情况下关闭连接。

即使您正确使用它们,即使是枚举器的内置扩展也可能导致这种情况:

 foreach (MyType item in GetMyTypeObjects().Take(10)) { ... } 

这不是我会遵循的模式。 我不会像在锁定时那样担心服务器上的负载。 遵循这种模式将数据检索过程集成到您的业务逻辑流程中,这似乎是一个全面解决问题的方法; 你不知道在迭代方面会发生什么,而你正在插入它。 一次性检索数据,然后在关闭阅读器后允许客户端代码对其进行枚举。

我建议不要预先优化。 在许多情况下,将汇集连接。

我也不希望SQL Server上的负载有任何差异 – 查询已经编译完毕,并且将会运行。

我担心的是你将自己完全置于客户端代码的怜悯之下。

您已经提到调用代码可能会使连接打开的时间超过严格必要的时间,但也有可能永远不会允许关闭/处置对象。

只要客户端代码使用foreachusing etc或显式调用枚举器的Dispose方法,那么你很好,但没有什么可以阻止它做这样的事情:

 var e = GetMyTypeObjects().GetEnumerator(); e.MoveNext(); // open the connection etc // forget about the enumerator and go away and do something else // now the reader, command and connection won't be closed/disposed // until the GC kicks in and calls their finalisers