0

以下のコードをご覧ください。

Private objCommand As SQLCommand 

Public Overrides Function ExecuteDataReader(ByVal strCommandType As String, ByVal sqlCommandString As String) As DbDataReader
    Dim objDR As SqlDataReader

    Try
        _objCon = getConnection()
        _objCon.Open()
        Using _objCon
            Using _objCommand
                _objCommand.Connection = _objCon
                _objCommand.CommandText = sqlCommandString
                _objCommand.CommandType = strCommandType
                objDR = _objCommand.ExecuteReader
                ExecuteDataReader = objDR
            End Using
        End Using
    Catch ex As Exception
        Throw
    Finally
        _objCon = Nothing
        _objCommand.Dispose()
        _objCommand = Nothing
        objDR = Nothing
    End Try
End Function

DataReader、接続オブジェクトが閉じられると閉じられるため、closed が返されます。DataReader接続オブジェクトはどのように存続できますか?

同様の質問を探したところ、次の質問が見つかりました: DataReader not closed when Connection is closed, results? . しかし、それは私の特定の質問には答えません。

4

2 に答える 2

2

Using接続が途中で閉じられる理由は、ブロックを終了するまで関数から戻らないためです。Using ブロックを離れると、接続がすぐに強制的に閉じられます。返されたオブジェクトとして datareader を設定するだけでは不十分です。明示的な Return ステートメントを使用しても十分ではありません...関数を離れると、Using ブロックを離れることを意味するため、データリーダーを使用する前に接続が閉じられます。


これらすべてを回避するために、次のようなパターンを使用します。

Public Iterator Function ExecuteDataReader(Of T)(ByVal sql As String, ByVal addParams as Action(Of SqlParameterCollection), ByVal castRow As Funnction(Of IDataRecord, T)) As IEnumerable(Of T)

    Using cn As SqlConnection = getConnection(), _
          cmd As New SqlCommand(sql, cn)

        addParams(cmd.Parameters)
        cn.Open()

        Using rdr As SqlDataReader = cmd.ExecuteReader()
            While rdr.Read()
                Yield castRow(rdr)
            End While
        End Using
    End Using
End Function

次に、その関数を次のように呼び出します。

Dim results As IEnumerable(Of Customer) = ExecuteDataReader( _
           "SELECT * FROM Customer WHERE Sales> @MinSales", _
      Sub(p) p.Add("@MinSales", SqlDbType.Double).Value = 10000.0, _
      Function(r) New Customer() With {Name=r("Name"), Address=r("Address"), Sales=r("Sales") })

For Each c As Customer in results
   '...
Next

混乱する可能性のあるものがあるため、そのパターンを少し見てみましょう...つまり、デリゲート引数をカバーしたいのです。

最初は addParameter 引数です。パラメータ情報を送信する方法が他にないため、元のパターンがひどく壊れていることを理解する必要があります。それは大きな問題です。ありがたいことに、それは簡単に解決されます。これが addParameter 引数の目的です。これを行う唯一の方法ではありません — つまり、キー/値/型の配列を渡すのと同じくらい簡単なことを行うこともできます — しかし、配列を通過する作業の重複や、パラメーター データを格納するメモリの重複を避けることができるため、私はこの方法が気に入っています。二回。

次は castRow 引数です。これがないと、例で見られる同様の問題に遭遇するため、これが必要です。ここでもコードは実行されますが、他の場所で同じオブジェクト コードを生成し続けるため、結果の最終レコードですべてが機能することになります。このようにして、正しい期待される結果が得られ、厳密に型指定された方法で結果が得られます。

続けて、Iterator と Yield のキーワードに既に慣れていることを願っていますが、これらは VB.Net に比較的慣れていないため、そうでなくても問題ありません。コンパイラがこのコードを datareader オブジェクトを反復処理できるものに変換することを知っておいてください。

于 2012-11-16T18:03:41.870 に答える
0

ExecuteReader のオーバーロードを使用して CommandBehaviour.Close を渡すと、リーダーが残りの部分を破棄すると、残りの部分は処理されなくなりますが、面倒になり、ライフタイムを制御できなくなります。

私の推奨事項は、これを行わないことです.1つのプロジェクトで行いました.軽量アクセスとしてDataReaderが好きで、Datatableが嫌いですが、レーダーを渡さないので、エラーの範囲が広すぎます. これは、コード、接続プーリング、ガベージ コレクターの間の絶え間ない戦いであり、勝つことはできません。

DataTable、EF、Linq、または昔ながらの IEnumberable を使用する

于 2012-11-16T18:04:50.713 に答える