帳票出力にLINQを活用する(CSVファイルに含まれたデータの並べ替え・抽出)
ここまではウォーミングアップということで、LINQを使った簡単なサンプルを紹介しました。ここからは、次はもう少し実用的なサンプルを紹介したいと思います。
LINQを使うと、これまでSQLでやっていたようなデータの並べ替えや抽出、複数データソースの結合といった操作を、データソースの種類を問わず簡潔に記述できます。
CSVファイルに含まれたデータの並べ替え・抽出
まずは、帳票のデータソースとして使用することも多いCSVファイルを、LINQで操作するサンプルを紹介します。
以前の記事では、CSVファイルをデータソースにして帳票を出力する方法について紹介しましたが、そのときはStreamReaderを利用してファイルを一気に読み取るだけの形だったため、ファイル内のデータ順序は決まっているものとして説明していました。
これまではファイルに格納されたデータを並べ替えたり、条件に一致したデータだけを抽出するためにファイルの各行を何らかのオブジェクトに変換し、それをいったんListに格納してから並べ替えたりといったコードを記述する必要がありましたが、LINQを使うとファイルに格納されたデータの並べ替えや抽出を簡単に書くことができます。
以下のデータは今回使用するCSVファイルの内容です。このファイルには商品情報が含まれていて、1件分のデータは「商品コード」「商品名称」「単価」の3要素で構成されています。ここでは、CSVファイルが「C:\ActiveReports3」というフォルダに保存されているものとします。
A12358847,USBフラッシュメモリ(2GB) ,4980 A12318181,USBフラッシュメモリ(8GB) ,9980 A22175175,ブロードバンドルータ,24800 A71351582,ストレートLANケーブル(10m),1650 A47005782,20インチワイド液晶ディスプレイ,39800 A99157000,外付型DVDドライブ,13800 A72002487,A3対応インクジェットプリンタ,49800 A35365587,光学式5ボタンマウス,6500 A23548783,IEEE802.11g/b対応 無線LANアクセスポイント,7800 A55487527,A4対応モノクロレーザープリンター,29800
このCSVファイルに対し「ファイルを読み込んで、単価の高いものから5件ぶんを降順で表示する」というコードを作成してみましょう。
Detailセクションの、商品コードと商品名称を表示する位置にLabelコントロールを2つ配置し、DataFieldプロパティにはそれぞれ「ProductCode」「ProductName」を設定します。単価の部分は金額の書式表示としたいのでTextBoxコントロールを使用し、DataFieldプロパティには「UnitPrice」を、OutputFormatプロパティには「##,##0」を設定します。
以下のコードはAcviteReportsプロジェクトのForm_Loadメソッドに、CSVファイルへアクセスするLINQのクエリを追加したものです。
ファイルをStreamReaderで読み出して文字列のリストに変換する部分はサブルーチンとして別メソッドに切り出しています。取り込んだ各行はカンマで区切られた複数のデータを表す文字列なので、これを配列の形に変換します。LINQクエリの中でletキーワードを使い文字列をSplitメソッドで分解した結果を配列の形で保持しておくと、続きの部分を簡潔に書くことができます。
private void Form1_Load(object sender, EventArgs e) { NewActiveReport1 rpt = new NewActiveReport1(); string fileName = @"C:\ActiveReports3\Products.csv"; using (StreamReader reader = File.OpenText(fileName)) { var query = from line in GetLines(reader) // 各行をカンマで区切る let items = line.Split(',') // 単価の高い順に並べ替え orderby Convert.ToInt32(items[2]) descending select new { ProductCode = items[0], ProductName = items[1], UnitPrice = items[2] }; query = query.Take(5); // 結果から先頭5件分だけ取得 rpt.DataSource = result.ToList(); reader.Close(); } rpt.Run(); this.viewer1.Document = rpt.Document; } //StreamReaderで取得したデータをリストに変換するサブルーチン private IEnumerable<string> GetLines(StreamReader r) { while (!r.EndOfStream) { string line = r.ReadLine(); yield return line; } }
Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim rpt As New NewActiveReport1 Dim fileName = "C:\ActiveReports3\Products.csv" Using reader As New StreamReader(fileName) Dim query = From lines In GetLines(reader) _ Let items = lines.Split(",") _ ' 各行をカンマで区切る Order By Convert.ToInt32(items(2)) Descending _ ' 単価の高い順に並べ替え Select New With{ _ .ProductCode = items[0], _ .ProductName = items[1], _ .UnitPrice = items[2] _ } query = query.Take(5) ' 結果から先頭5件分だけ取得 rpt.DataSource = query.ToList() reader.Close() End Using rpt.Run() Me.Viewer1.Document = rpt.Document End Sub 'StreamReaderで取得したデータをリストに変換するサブルーチン Private Function GetLines(ByRef Reader As StreamReader) _ As IEnumerable(Of String) Dim result As New List(Of String) While Reader.EndOfStream <> True Dim line = Reader.ReadLine() Debug.WriteLine(line) result.Add(line) End While Return result End Function
コードの記述が終わったら、さっそくアプリケーションを実行してみましょう。以下のように、単価の高い商品5件だけが表示されるはずです。