.NETで作る!

.NETに関するあれこれ(C#、VB.NET)

ExecuteReader って終了分岐が気持ち悪いから拡張メソッドでForEach文で書けるようにする

DBアプリケーションで避けては通れない ExecuteReader メソッドですが、これってイマイチですよね。 ひとまず適当にサンプル書いてみます。

Private Sub SelectRader()
    Using cn As IDbConnection = New SqlClient.SqlConnection("Data Source=.\SQLEXPRESS;Initial Catalog=AdventureWorks2016CTP3;Integrated Security=SSPI")
        cn.Open()
        Dim sql = "select * from Person.Person where BusinessEntityID < 1000 order by BusinessEntityID"
        Using cmd As IDbCommand = New SqlClient.SqlCommand(sql, cn)
            Using r = cmd.ExecuteReader
                Do While r.Read()
                    Console.WriteLine(r("BusinessEntityID"))
                Loop
            End Using
        End Using
    End Using
End Sub

これの何がいやってForEach文で書けない

Loop文の条件がイマイチわかりづらい。覚えりゃいいんですけどなんか覚えたくない。 ForEach文で書けたらそんなこと気にする必要もない。 ForEach文で書けないかなぁ…

拡張メソッドで解決だ

拡張メソッドでラップしてやればよさげ! まずは完成形をイメージしてみる。

これが

Using r = cmd.ExecuteReader
    Do While r.Read()
        Console.WriteLine(r("BusinessEntityID"))
    Loop
End Using

こんな感じになれば!

For Each r In cmd.ExecuteReader
    Console.WriteLine(r("BusinessEntityID"))
Next

拡張メソッドはこんな感じ

ExecuteReaderという名前だと通常のものと紛らわしくなるので別名を付けますか。 他のメソッド名とそろえると「ExecuteDataIterator」ってことにします。

<Extension>
Public Iterator Function ExecuteDataIterator(source As IDbCommand) As IEnumerable(Of IDataReader)
    Using r = source.ExecuteReader
        Do While r.Read()
            Yield r
        Loop
    End Using
End Function

適用してみるとこんな感じ

Private Sub SelectRader()
    Using cn As IDbConnection = New SqlClient.SqlConnection("Data Source=.\SQLEXPRESS;Initial Catalog=AdventureWorks2016CTP3;Integrated Security=SSPI")
        cn.Open()
        Dim sql = "select * from Person.Person where BusinessEntityID < 1000 order by BusinessEntityID"
        Using cmd As IDbCommand = New SqlClient.SqlCommand(sql, cn)
            For Each r In cmd.ExecuteDataIterator
                Console.WriteLine(r("BusinessEntityID"))
            Next
        End Using
    End Using
End Sub

戻り値は使い慣れた IDataReader なので覚えることはあまりないです。 字下げも減るし、コード量も減るし、終了分岐を間違えることもなくていい感じ。

余談

突き詰めるとここまで行けるはず!っていうか、自分は最近コレで書いてます。

Private Sub SelectRader()
    Dim sql As String = "select * from Person.Person where BusinessEntityID < 1000 order by BusinessEntityID"
    For Each r In sql.ExecuteDataIterator
        Console.WriteLine(r("BusinessEntityID"))
    Next
End Sub

コネクション開閉、DBコマンド生成処理をすべて拡張メソッドにやらせてます。 コーディング量は約半分!

. .