レコード全件取得
まずは何もせず全件取得のケースから始めます。ここではまだLINQは使用していません。
using (var context = new CodeZineSampleContext()) //(1)コンテキストを生成 { IQueryable<Product> products = context.Products; //(2)全件取得 DumpProducts(products); //(3)取得したエンティティを出力 } ・・・ //エンティティのダンプメソッド private static void DumpProducts(IQueryable<Product> products) { foreach (var product in products) //1件ずつ取り出して { //(4)商品(Products)、出荷元(Makers)、担当者(Employees) // 部署(Departments)の情報をダンプ Console.WriteLine( "Product Name: {0}, Price : {1}\n Maker Name: {2}\n Employee Name: {3}, Department: {4}", product.Name, product.Price, product.Maker.Name, product.Employee.Name, product.Employee.Department.Name); } }
(1)では、Entity Frameworkの作法に則ってコンテキストを生成しています。このコンテキストクラスから各テーブルへのクエリを行います。
(2)では、コンテキストのProdcutsプロパティから、Productsテーブルの全件を取得しています。戻り値がIQueryable<Product>となっていますが、これは「データベースでクエリを実行し、結果として返ってくるProduct一覧」を指しています。
(3)では、取得したProductエンティティ一覧を(4)のDumpProductsメソッドに投げて、データの内容を出力しています。
(4)では、foreachで1件ずつ取り出したProductエンティティについて、商品(Products)、出荷元(Makers)、担当者(Employees)、部署(Departments)の情報をコンソールに出力しています。
結果はリスト2のようになります。
Product Name: ショートケーキ, Price : 250 Maker Name: お菓子屋さんA Employee Name: 土井, Department: 営業部 ・・・
なお、「Productsテーブルの全件取得しているはずなのに、他のテーブルの内容まで取り出せているのはなぜだろう?」という問いは、第3回まで取っておきましょう。じっくり考えたい良問です。
LINQメソッド式でデータベースクエリを書いてみよう
ようやく本題のLINQメソッド式を使ったクエリまで来ました。
まずは小手調べに、特定の金額未満の商品を出力してみます(リスト3)。
products = context.Products.Where(x => x.Price < 300); DumpProducts(products);
ここではWhereメソッドでフィルタしており、ラムダ式は「x => x.Price < 300」となっています。xのデータ型が指定されていませんが「context.ProductsはDbSet<Product>というProductのコレクションなので、一件ずつ取り出して処理するなら当然xはProductのはず」という型推論が働いています。したがって、「x.」と書いた時点で図1のようにProductのメンバがIntelliSense表示されます。
また、ミスタイプすれば図2のようにエラー表示してくれます。
このように、Entity FrameworkとLINQを使えば「データベースのフィールド名忘れちゃった。定義書どこだっけ……このフィールドのデータ型は何だっけ……」といったデータベース開発でありがちな悩みとは、すっきりおさらばできます。
文字列部分一致検索を書いてみる
続いて、文字列部分一致検索の場合はリスト4のようになります。
products = context.Products.Where(x => x.Employee.Name.Contains("土")); DumpProducts(products);
ここでは「土」という文字が担当者名に含まれている商品だけをフィルタしています。ラムダ式が少し複雑になっていますが、「Product(商品)->Employee(担当者)->Name(担当者名文字列)->string型のContainsメソッド」と流れています。別テーブル(Employees)のフィールドでの絞り込みも自由自在です。string型のContainsメソッドを使っていますが、これはデータベース上で「LIKE '%土%'」のようなSQLに変換されて実行されますので、文字列部分一致検索も簡単に書くことができます。「LIKE条件を%で挟むの忘れた……」なんてお約束のミスもおさらばです。
特定の値のいずれかに当てはまるものを検索する
もう一つ、「特定の価格リストに当てはまるものだけ検索」も見てみましょう(リスト5)。
var list = new List<int>() { 100, 200, 300, 400, 500 }; products = context.Products.Where(x => list.Contains(x.Price)); DumpProducts(products);
今回はあらかじめ金額をList<int>で定義しておき、ラムダ式の中では「list.Contains(x.Price)」として、「listの中にx.Priceが含まれているかどうか」という条件を書いています。これもデータベース上では「IN (100, 200, 300, ...)」というIN演算子を使ったSQLに変換されます。SQLを文字列連結で作る場合の「IN演算子の後に1個ずつカンマで挟んで出力しなきゃ。あ、末尾にカンマを付けちゃったよ……」といった凡ミスも避けられます。
ここで書いたのはシンプルなクエリですが、他の言語環境、フレームワークでありがちなミスが起きづらい、プログラマにとって非常に快適な環境であることがお分かりいただけるでしょう。繰り返しになりますが、これはLINQが「言語に統合されたクエリである=クエリ自体がコンパイル可能である」ことによるメリットです。
ここでは数値比較、文字列部分一致、リストに含まれるか、という条件を使いましたが、LINQが実際にどのようなSQLに変換されるかについては、第3回で詳細に解説します。