产生返回和方法状态



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

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

是否有一种方法来提取这个逻辑的方式,我这样做,或者我只剩下内联这个特定的方法,忽略重复?

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

我喜欢yield return版本的简洁,但我认为最终最好不要从DB加载。

一次又一次地返回相同的reader副本有什么意义呢?

<<p> 错误的逻辑/strong>
using (IDataReader reader = command.ExecuteReader())
   if (reader != null)
      while (reader.Read())
         yield return reader;

你所做的是多次返回相同的DataReader对象。

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

伪代码

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;
      }

首先,我认为你应该使你的方法泛型。也就是说,你的方法不应该返回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。)

最新更新