对SQL数据使用IEnumerable <t>返回类型是否存在任何缺陷?</t>

时间:2009-09-09 14:39:31

标签: c# sql-server ienumerable

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

public IEnumberable<MyType> 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<MyType>(reader);
         }
       }
    }
  }
  yield break;
}

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

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

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

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

  •   -

4 个答案:

答案 0 :(得分:6)

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

答案 1 :(得分:2)

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

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

答案 2 :(得分:2)

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

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

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

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

答案 3 :(得分:1)

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

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

只要客户端代码使用foreachusing等或明确调用枚举器的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