代码之家  ›  专栏  ›  技术社区  ›  scottm

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

  •  5
  • scottm  · 技术社区  · 15 年前

    我的问题是基于以下代码的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并返回一个列表或其他内容)。

    • 有没有什么实例可以让你想到你不想以这种方式使用IEnumerable,或者你认为它完全适合的实例?

    • 这会给SQL Server带来什么样的负载?

    • 这是你在自己的代码中会用到的东西吗(除非有提到NHibernate,亚音速等)?

    • -
    4 回复  |  直到 15 年前
        1
  •  2
  •   Guffa    15 年前

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

    当您读取最后一条记录之后,连接对象将被关闭,并且如果在此之前停止读取,连接对象将不会被释放。例如,如果您知道一个结果中总是有十条记录,并且只有一个循环可以从枚举器中读取这十条记录,而不进行第十一个读取调用(在最后一项之外读取),则连接不会正确关闭。另外,如果您只想使用结果的一部分,那么在不读取其余记录的情况下,就无法关闭连接。

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

    foreach (MyType item in GetMyTypeObjects().Take(10)) {
       ...
    }
    
        2
  •  6
  •   Adam Robinson    15 年前

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

        3
  •  2
  •   John Saunders    15 年前

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

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

        4
  •  1
  •   LukeH    15 年前

    我担心的是,您完全听任客户机代码的摆布。

    您已经提到调用代码可能会比严格需要的时间更长地保持连接打开,但也有可能它永远不会允许关闭/释放对象。

    只要客户端代码使用 foreach , using 或显式调用枚举器的 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