連載:[完全版]究極のC#プログラミング

Chapter17 LINQ to SQL

川俣 晶
2010/04/14

17.4 LINQ to SQLのサンプル

 本書は、あくまでC# 3.0入門である。LINQ to SQLは、C# 3.0の外部に存在する独立した技術であり、Visual Basicなどでも利用されるものである。そのため、本書ではC# 3.0プログラマーとLINQ to SQLの関わりを示し、簡単なサンプルを提示する程度にとどめる。前者についてはすでに述べたので、あとは簡単なサンプルを1つだけ提示して終わろう。とはいえ、C#プログラマーとして見るべきところは多い。

 ここでは、図17.3図17.4のようなテーブルPointListが存在するという前提で始めよう。


図17.3 PointListテーブルの設計

 以降では、このテーブルに含まれる、pointが80以上の人を検索してみよう。

 まず、Visual Studio 2008でコンソールアプリケーションのプロジェクトを作成する。次に、このプロジェクトに「LINQ to SQLクラス」を追加する。これはテンプレートから選ぶだけなので簡単だろう。

図17.4 PointListテーブルの内容

 追加すると即座にO/Rデザイナが開かれて、図17.5のような表示になる。

図17.5 LINQ to SQLクラス追加時に開くO/Rデザイナ

 これを使って、O/Rマッピングを作成してみよう。やり方は簡単で、サーバーエクスプローラ上の[PointList]という項目を中央の領域へドラッグ&ドロップするだけである(図17.6参照)。

 これでコードを書く準備はできた。Program.csにリスト17.1のようなコードを書いてみよう。

図17.6 生成されたO/Rマッピング

using System;
using System.Linq;

namespace ConsoleApplication78
{
  class Program
  {
    static void Main(string[] args)
    {
      var db = new DataClasses1DataContext();

      var query = from n in db.PointList
                  where n.point >= 80
                  select n;

      foreach (var m in query)
      {
        Console.WriteLine("{0} {1} {2}points",
          m.name,m.date.ToString("yyyy/MM/dd"),
          m.point);
      }
      // 出力
      // 花子     2009/02/28 90points
      // 三郎     2008/12/31 100points
    }
  }
}
リスト17.1 LINQ to SQLによるデータベースの検索

 ここで、DataClasses1DataContextクラスはO/RマッピングとしてO/Rデザイナが自動生成してくれたクラス名である。これを使えば、db.PointListと書くだけでPointListテーブルに対するマッピングを参照でき、それはクエリ式でクエリできる。

 ちなみに、ここでは「m.date.ToString("yyyy/MM/dd")」の部分に注目しよう。この書式指定付きToStringメソッドはDateTime構造体に固有のものである。このようなコードが記述可能であるのは、PointListテーブルのdate列がDateTime型のプロパティにマッピングされているからである。つまり、SQLの型とC#の型がマッピングによって連携しており、型に関するトラブルが飛躍的に起こりにくくなっている。

 さて、ここで注意すべき点がある。このクエリ式は、実際にはC#プログラムとしては実行されていない点である。つまり、「n.point >= 80」という式は、C#プログラムとして実行されることはない。

 常識的なC#プログラマーの感覚でいえば「そんなばかな」と思うかもしれないが、それは事実である。では、どのようにしてそれを証明できるだろうか?

 その方法は簡単で、変数dbをnewしている行の次に、次に示した行を挿入するだけである。

db.Log = Console.Out;

 これで、どのようなSQL文がSQL Serverに送信されているかが手に取るようにわかる。

SELECT [t0].[id], [t0].[name], [t0].[date], [t0].[point]
FROM [dbo].[PointList] AS [t0]
WHERE [t0].[point] >= @p0
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [80]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1

花子     2009/02/28 90points
三郎     2008/12/31 100points
リスト17.1でSQL Serverに送信されているSQL文

 この3行目は、pointが「@p0」以上である場合という条件を示し、4行目で、@p0は80であることが示されている。つまり、このクエリの中には「n.point >= 80」という条件そのものが埋め込まれ、SQL Server側で処理されているのである。

 このことは、クエリがサーバー側で処理され、結果だけが返ってくることを示す。大量のデータをまず取得して検索を行うようなネットワークの無駄は排除されている。

 一方で、このような仕掛けが成立する理由は、本章の冒頭でも説明したとおり、「式本体を持つラムダ式は、式ツリーに変換できる」というC# 3.0の機能にある。LINQのクエリ式を式ツリーに変換したうえで、それをさらにSQL文に翻訳しているわけである。

 このことは、式ツリーに翻訳できてもSQL文に翻訳できない式は扱えないことを示す。たとえば、条件判定がローカル上のメソッドに依存するような式はSQL文に翻訳できない。SQL Server側に同等のメソッドが存在しないからである。

 たとえば、Programクラスに次のメソッドを追加してみよう。

private static bool check(int n)
{
  return n >= 80;
}

 そして、クエリを次のように書き直してみよう。

var query = from n in db.PointList
            where check(n.point)
            select n;

 このプログラムは構文的には正しいのでコンパイル可能である。しかし、実行すると次のような例外を出して止まる。

System.NotSupportedException はハンドルされませんでした。
メソッド 'Boolean check(Int32)' には、サポートされる SQL への変換がありません。

 SQL Server側からはcheckメソッドは直接呼び出せないし、仮にネットワークを越えて呼び出したとしても、時間がかかりすぎてクエリ速度が非現実的に遅くなるだけだろう。

 ただし、次のようなメソッドを用意した場合はクエリできる。

private static int get80()
{
  return 80;
}

 クエリは次のように記述する。

var query = from n in db.PointList
            where n.point >= get80()
            select n;

 この場合、get80メソッドはローカル上で計算可能であり、その計算結果である「80」をサーバーへ送ることでクエリは成立する。


 INDEX
  [完全版]究極のC#プログラミング
  Chapter17 LINQ to SQL
    1.17.1 効率的に列挙可能にするという問題
    2.17.2 SQL Serverのワナ
    3.17.3 LINQ to SQLという突破口
  4.17.4 LINQ to SQLのサンプル
    5.17.5 LINQ to SQLとメソッド構文
    6.17.6 LINQ to SQLのまとめ/練習問題
 
インデックス・ページヘ  「[完全版]究極のC#プログラミング」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間