web-dev-qa-db-ja.com

データリーダーオブジェクトを反復処理しているときに、接続の状態と「yield」によるコードパフォーマンスへの影響について疑問に思っています。

データベースからデータをフェッチするために使用しているサンプルコードは次のとおりです。DAOレイヤー上:

public IEnumerable<IDataRecord> GetDATA(ICommonSearchCriteriaDto commonSearchCriteriaDto)
{
    using(DbContext)
    {
        DbDataReader reader = DbContext.GetReader("ABC_PACKAGE.GET_DATA", oracleParams.ToArray(), CommandType.StoredProcedure);
        while (reader.Read())
        {
            yield return reader;
        }
    }
}

BOレイヤーでは、上記のメソッドを次のように呼び出しています。

List<IGridDataDto> GridDataDtos = MapMultiple(_costDriversGraphDao.GetGraphData(commonSearchCriteriaDto)).ToList();

マッパーレイヤーのMapMultipleメソッドは次のように定義されます。

public IGridDataDto MapSingle(IDataRecord dataRecord)
{
    return new GridDataDto
    {
        Code = Convert.ToString(dataRecord["Code"]),
        Name = Convert.ToString(dataRecord["Name"]),
        Type = Convert.ToString(dataRecord["Type"])     
    };
}
public IEnumerable<IGridDataDto> MapMultiple(IEnumerable<IDataRecord> dataRecords)
{
    return dataRecords.Select(MapSingle);
}

上記のコードはうまく機能していますが、上記のコードに関する2つの懸念について疑問に思っています。

  1. データリーダーの接続はどのくらいの期間開かれますか?
  2. コードのパフォーマンス係数のみを考慮する場合、レコードをリストに追加してリスト全体を返すのではなく、「yield return」を使用することをお勧めしますか?
16
Sanjeev Rai
  1. コードには、接続を開いたり閉じたりする場所が表示されません。しかし、リーダーここでは実際には開いているだけですデータを反復している間。遅延実行など。これを行うコードのビットは.ToList()だけなので、問題ありません。より一般的なケースでは、はい。リーダーは、反復するのにかかる時間だけ開いています。 .ToList()を実行すると最小限になります。 foreachを実行し、(すべてのアイテムに対して)外部httpリクエストを作成して20秒待つと、はい-それはより長く開かれます。
  2. どちらにも用途があります。バッファなしのアプローチは、ストリームとして処理したい巨大な結果に最適です。単一のメモリ内リストにロードする必要はありません(または、一度にすべてをメモリに保存する必要もありません)。リストを返すと、接続がすばやく閉じられ、リーダーが既に開いているときに誤って接続を使用することを簡単に回避できますが、大きな結果には理想的ではありません。

イテレータブロックを返すと、呼び出し元は何が正常であるかを判断できます。常にリストを返す場合、それらには多くのオプションがありません。 3番目の方法(dapperで行う)は、選択を彼らのものにすることです。オプションのboolパラメータがあり、デフォルトは「リストを返す」ですが、呼び出し元は「イテレータブロックを返す」ことを示すように変更できます。基本的に:

bool buffered = true

パラメータ内、および:

var data = QueryInternal<T>(...blah...);
return buffered ? data.ToList() : data;

実装で。ほとんどの場合、リストを返すことは完全に合理的であり、多くの問題を回避するため、これをデフォルトにします。

15
Marc Gravell

データリーダーの接続はどのくらいの期間開かれますか?

接続は、readerが閉じられるまで開いたままになります。つまり、反復が終了するまで開いたままになります。

コードのパフォーマンス係数のみを考慮する場合、レコードをリストに追加してリスト全体を返す代わりに、yield returnを使用することをお勧めしますか?

これはいくつかの要因に依存します:

  • 結果全体をフェッチする予定がない場合は、yield returnを使用するとネットワーク上で転送されるデータの量を節約できます。
  • 返されたデータをオブジェクトに変換する予定がない場合、または複数の行を使用して単一のオブジェクトを作成する場合、yield returnは、プログラムのピーク使用ポイントで使用されるメモリを節約するのに役立ちます
  • エンチャー結果セットを短期間で反復する予定の場合、yield returnを使用してもパフォーマンスが低下することはありません。複数の同時スレッドで反復がかなりの時間続く場合、RDBMS側で開いているカーソルの数を超える可能性があります。
4
dasblinkenlight

この回答は、示されている実装の欠陥を無視し、一般的な考え方をカバーしています。

これはトレードオフです-システムの制約を知らずにそれが良い考えであるかどうかを判断することは不可能です-あなたが得ると期待するデータの量、あなたが受け入れることをいとわないメモリ消費、データベースの予想される負荷、等

0