LINQを使ったクエリの結果を帳票に表示する
.NET Framework 3.5に対応したVisual Studio 2008の目玉機能と言えば、なんといってもLINQ(Language INtegrated Query:言語統合クエリ)ではないでしょうか。
これまではRDBMSのテーブルに格納されたデータやオブジェクトのコレクション、XMLドキュメントなど、データソースの種類ごとに異なったデータアクセスコードを記述する必要がありました。.NET Framework 3.5では新しいLINQの構文を活用することで、データソースの種類を気にせず、統一的な問い合わせコードでデータを操作できるようになります。
ActiveReportsも、VS2008への対応と合わせてLINQを使ったデータの取り扱いに対応しました。今後は帳票として出力するデータの操作にLINQを活用するシーンも増えてくるのではないかと思います。
LINQを使うための準備
これから紹介するサンプルコードでは、冒頭で紹介した「ActiveReportsアプリケーション」のプロジェクトテンプレートを使用しますが、ここで1つ、気をつけたいポイントがあります。
このテンプレートで作成したプロジェクトのForm.csやProgram.csには、LINQを使用するための名前空間が設定されていません。プロジェクトをLINQのコードを記述するソースファイルを開いて、先頭にusingディレクティブ(VBの場合はImportsステートメント)を追加して、LINQを使えるようにしましょう。
using System.Linq;
Imports System.Linq
参照を追加したところで、さっそくLINQを利用した帳票作成を始めたいと思います。
ActiveReportsでLINQを使うときの原則
ActiveReportsでは、帳票の出力元となるデータソースを、DataDynamics.ActiveReports.ActiveReport3オブジェクトのDataSourceプロパティに設定します。DataSourceプロパティの型はobject型になっているので、一見どんな型のオブジェクトを指定してもよさそうに見えますが、LINQによるクエリの結果をそのままデータソースとして使うことはできません。
LINQのクエリ結果はSystem.Collections.Generics.IEnumerable<T>インターフェースを実装した型で返されます。ActiveReportsでLINQの問い合わせ結果をデータソースとして使うためには、問い合わせ結果をToListメソッドでリストの型(System.Collections.Generics.List<T>)に変換して渡します。
LINQにはオブジェクトコレクションを操作するための「LINQ to Object」、データベースに格納されたデータにアクセスするための「LINQ to SQL」、XMLドキュメント用の「LINQ to XML」の3種類がありますが、どのタイプのLINQでもこのルールは同じです。
Enumerable.Rangeで作った列挙を表示させる
それでは、まず最初は簡単な例でLINQを試してみましょう。
System.Linq.Enumerableクラスには、IEnumerable<T>インターフェースを実装した各種データソースを操作するためのさまざまなメソッドが提供されています。Enumerable.Rangeメソッドは、指定された範囲の整数のシーケンスを返します。
例えばEnumerable.Range(1, 10)と書くと、1から10までの数値の列挙がIEnumerableインターフェースで返されます。今回は、このデータの集まりに対して「4より大きな値」という検索条件を指定し、結果として「Countプロパティに数値を含む匿名クラス」を生成してその集合を返すクエリを書いてみましょう。
VS2008で、冒頭に紹介した「ActiveReportsアプリケーション」のプロジェクトを新規作成し、デフォルトで作成されるフォームのLoadイベントに、以下のようにLINQのコードを追加してみましょう。
private void Form1_Load(object sender, EventArgs e) { NewActiveReport1 rpt = new NewActiveReport1(); //クエリ元のデータを指定 var query = from x in Enumerable.Range(1, 10) where x > 4 //検索条件を指定 select new {Count = x}; //匿名クラスを生成 rpt.DataSource = query.ToList(); rpt.Run(); this.viewer1.Document = rpt.Document; }
Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim rpt As New NewActiveReport1 ' クエリ元のデータを指定 Dim query = From x In Enumerable.Range(1, 10) _ Where x > 4 _ ' 検索条件を指定 Select New With {.Count = x} ' 匿名クラスを生成 rpt.DataSource = query.ToList() rpt.Run() Me.Viewer1.Document = rpt.Document End Sub
なお、このコードの問い合わせ部分はLINQで記述する以外にも、IEnumerableインターフェースに定義されたメソッドを連結する形(メソッドチェイン)で書くことができます。また、条件式の部分にはC#/Visual Basicの新機能「ラムダ式」を使用することもできます。
以下は、今紹介したLINQのクエリ部分を、メソッドチェインとラムダ式で書きかえたものです。見た目は少し違いますが、どちらも同じ意味になります。
private void Form1_Load(object sender, EventArgs e) { NewActiveReport1 rpt = new NewActiveReport1(); //クエリ元のデータを指定 var query = Enumerable.Range(1, 10) .Where(x => x > 4) .Select(x => new {Count = x}); rpt.DataSource = query.ToList(); rpt.Run(); this.viewer1.Document = rpt.Document; }
Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim rpt As New NewActiveReport1 ' クエリ元のデータを指定 Dim query = Enumerable.Range(1, 10) _ .Where (Function(x) x > 4) _ .Select(Function(x) New With {.Count = x}) rpt.DataSource = query.ToList() rpt.Run() Me.Viewer1.Document = rpt.Document End Sub
帳票レイアウト側の設定と実行
レポートファイル側では、LINQによって返される予定のオブジェクトを表示するために、Labelコントロールを1つ配置します(以下の帳票レイアウトの水色の部分)。LabelコントロールのDataFieldプロパティには、クエリの中で作成した匿名クラスで定義した「Count」プロパティを設定してください。
コードの記述と帳票レイアウトの設定が終わったら準備完了です。
F5キーを押してプロジェクトを実行すると、フォームのViewerコントロールには次のような結果が表示されます。
1~10の整数のうち、where句で記述した検索条件(4より大きい)にマッチする6件のデータだけが表示されているのがわかります。