收益率和方法状态

时间:2013-06-08 01:07:19

标签: c# database .net-4.0 yield-return

我试图从我的数据库执行方法中删除一些重复。我有一堆具有以下结构的方法:

IDbConnection connection = mConnections[pConnectionID];
bool wasAlreadyOpen = connection.State == ConnectionState.Open;

try
{
   if (!wasAlreadyOpen)
      connection.Open();

   using (IDbCommand command = connection.CreateCommand())
   {
      command.CommandText = pSQL;
      if (pParams != null)
         ApplyParameters(pParams, command);
      // do something interesting with command
   }
}
finally
{
   if (!wasAlreadyOpen)
      connection.Close();
}

我使用此签名将此逻辑提取到另一个方法中:

private object ExecuteQuery(int pConnectionID, string pSQL,
   Func<IDbCommand, object> pQuery, IEnumerable<QueryParameter> pParams)

并在算法的// do something部分执行此操作:

return pQuery(command);

这似乎很有效,除了一个问题。在我的ExecuteReader方法中,查询代码如下所示:

using (IDataReader reader = command.ExecuteReader())
   if (reader != null)
      while (reader.Read())
         yield return reader;

问题似乎是yield return为懒惰执行而保存的“状态”仅取自包含yield语句的方法。如果我将上面的这四行提取到它自己的方法或匿名方法/ lambda,那么就没有足够的状态来在读取数据时保持数据库连接打开。

有没有办法按照我这样做的方式提取这个逻辑,或者我只留下内联这个特定方法并忽略重复?

3 个答案:

答案 0 :(得分:1)

我的解决方案是不要懒惰地从数据库加载。我觉得从懒惰地加载数据库并不是一个好主意。相反,我已向ExecuteReader方法添加了转换函数Func<IDataRecord, T>参数。然后,读取的数据记录会立即转换为对象,而不是期望调用者使用IEnumerable<IDataRecord>并对其执行某些操作。

我喜欢yield return版本的简洁,但我认为最好不要懒散地加载数据库。

答案 1 :(得分:1)

一次又一次地返回同一份阅读器有什么意义?

错误的逻辑

using (IDataReader reader = command.ExecuteReader())
   if (reader != null)
      while (reader.Read())
         yield return reader;

您正在做的是多次返回相同的DataReader对象。

您需要做的是从reader对象创建Class Object并返回读取数据。

伪代码

using (IDataReader reader = command.ExecuteReader())
   if (reader != null)
      while (reader.Read())
      {
           MyObject obj = new MyObject(reader.getInt32(0), reader.getString(1), reader.getFloat(2));
           yield return obj;
      }

答案 2 :(得分:0)

首先,我认为你应该让你的方法通用。也就是说,您的方法不应该返回object,它应该返回T

private T ExecuteQuery<T>(int pConnectionID, string pSQL,
   Func<IDbCommand, T> pQuery, IEnumerable<QueryParameter> pParams)

现在,我认为您应该做的是为集合添加另一个ExecuteQuery重载:

private IEnumerable<T> ExecuteQuery<T>(int pConnectionID, string pSQL,
   Func<IDbCommand, IEnumerable<T>> pQuery, IEnumerable<QueryParameter> pParams)

将使用yield return本身实现。类似的东西:

using (IDbCommand command = connection.CreateCommand())
{
   command.CommandText = pSQL;
   if (pParams != null)
      ApplyParameters(pParams, command);

    foreach (var x in pQuery(command))
        yield return x;
}

这样,只有在结果迭代完成后(或者如果它过早中止),命令才会被释放。

(我对此并不完全确定,但重载解析可能会为集合选择错误的重载。在这种情况下,将集合版本重命名为ExecuteQueryCollection。)