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

1つの問題を除いて、これはうまくいくようです。私のExecuteReader方法では、クエリ コードは次のようになります。

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

問題は、遅延実行のために保存される「状態」が、ステートメントyield returnを含むメソッドからのみ取得されることです。yield上記の 4 行を独自のメソッドまたは匿名メソッド/ラムダに抽出すると、データの読み取り中にデータベース接続を開いたままにするのに十分な状態がありません。

私がこれを行っている方法でこのロジックを抽出する方法はありますか、それともこの特定のメソッドをインライン化して重複を無視するだけですか?

4

3 に答える 3

1

これに対する私の解決策は、データベースから遅延ロードしないことです。とにかく、データベースから遅延してロードするのは良い考えではないと感じています。代わりに、ExecuteReader メソッドに変換関数Func<IDataRecord, T>パラメーターを追加しました。IEnumerable<IDataRecord>読み取られたデータ レコードは、呼び出し元が を取得して何かを実行することを期待するのではなく、すぐにオブジェクトに変換されます。

バージョンの簡潔さは気に入りましたが、yield return最終的には DB から遅延ロードしない方がよいと思います。

于 2013-06-08T01:07:19.270 に答える
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 に答える
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 に答える