0

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

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,那么在读取数据时没有足够的状态来保持数据库连接打开。

有没有办法以我这样做的方式提取这个逻辑,还是我只剩下内联这个特定方法并忽略重复?

4

3 回答 3

1

一次又一次地返回相同的阅读器副本有什么意义?

错误的逻辑

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

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

您需要做的是从读取器对象创建类对象并返回读取的数据。

伪代码

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;
      }
于 2013-06-08T01:13:14.973 回答
1

我对此的解决方案是不要从数据库中延迟加载。我觉得无论如何懒惰地从数据库加载并不是一个好主意。相反,我在 ExecuteReader 方法中添加了一个转换函数Func<IDataRecord, T>参数。读取的数据记录然后立即转换为对象,而不是期望调用者接受IEnumerable<IDataRecord>并对其进行处理。

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

于 2013-06-08T01:07:19.270 回答
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.)

于 2013-06-08T12:19:14.090 回答