書籍転載:ASP.NET MVC 5 実践プログラミング
LINQとは?[C#]
LINQ to Entitiesの説明に入る前に、まずはC#の言語機能として組み込まれたクエリ機能「LINQ」について簡単に紹介する。書籍転載の18本目(基礎編「5-3-1」)。
書籍転載について
本コーナーは、秀和システム発行の書籍『ASP.NET MVC 5 実践プログラミング』の中から、特にBuild Insiderの読者に有用だと考えられる項目を編集部が選び、同社の許可を得て転載したものです。
『ASP.NET MVC 5 実践プログラミング』の詳細や購入は秀和システムのサイトや目次ページをご覧ください。
ご注意
本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどはBuild Insiderのそれとは一致しません。あらかじめご了承ください。
5-3 LINQ to Entities
作成したデータモデルを介してデータベースにアクセスするには、大きくEntity SQL*16とLINQ to Entitiesが用意されていますが、本書では「言語にも統合されており、Intellisense機能の恩恵も受けやすい」という理由から、LINQ to Entitiesを採用します。
- *16 Transact-SQLから派生した言語で、概念モデル(エンティティ)に対する問い合わせで利用します。
5-3-1 LINQとは?
LINQ to Entitiesの前に、まずはLINQそのものについて軽く触れておきます。LINQとはLanguage INtegrated Queryの略で、統合言語クエリーと訳される場合もあります。ざっくりと言ってしまうならば、
オブジェクトやデータベース、データセット、エンティティ、XML文書など、アプリケーションで扱うさまざまなデータソースに対して、統一的な手段でアクセスするしくみ
です。
従来、これら種々のデータソースをアプリケーションから操作するには、複数の手段を使い分ける必要がありました。たとえば、データベースにアクセスするならばSQLを使いますし、XML文書を操作するならばDOM(Document Object Model)やXPath/XQueryを利用します。また、配列やリストを操作する場合には、foreachやwhileなどの反復構文を利用して走査するのが通例でしょう。
これらのさまざまなアプローチをひとつの手段に統合しようというのがLINQの考え方なのです。LINQを利用することで、アプリケーション開発者はデータソースの種類に関わらず、限りなく同じ方法でデータにアクセスできます。
LINQは、アクセスするデータの種類に応じてLINQ to Objects、LINQ to SQL、LINQ to XML、LINQ to DataSetなどに分類できますが*17、Entity Data ModelにアクセスするためのLINQのことをLINQ to Entitiesと呼びます。
- *17 これらはLINQプロバイダーと呼ばれます。プロバイダーはアクセスするデータソースに応じて動的に切り替わりますので、アプリケーション開発者がプロバイダーを意識する必要はほとんどありません。
LINQによる問い合わせは、大きくクエリー式構文とメソッド構文とに分類できます。大雑把に言ってしまうと、クエリー式構文は総じてシンプルに表現できますが、すべての問い合わせを表現できるわけではありません。メソッド構文はコードそのものはいささか冗長になりますが、LINQのすべての機能を表現できます。いずれを利用するかは(それが許される状況では)好みにもよりますが、適時適所で双方を理解できた方が良いので、本章でも双方をできるだけ併記していくことにします。
クエリー式構文
以下は、与えられたURLに対応する記事情報を取り出すためのLINQクエリーの例です。
var article= from a in db.Articles
where a.Url == 'https://www.buildinsider.net/web/jquerymobileref'
select a.Title;
|
SQL-SELECT命令によく似ていますが、順番が異なります。SQL命令であればSELECT...FROM...WHERE
の順番で記述するところですね。しかし、LINQによる問い合わせは、from句で始まり、select句で終わるのが基本です*18。SQL-SELECT命令と比べてしまうと、違和感を覚えるところかもしれませんが、要は慣れの問題ですのでそういうものだと覚えてしまいましょう。
- *18 正確にはgroup句で終わる場合もあります。
from句とselect句の間には、さまざまなデータ抽出/操作のためのキーワード(句)を追加できます。ここではデータ抽出の絞り込みを行うためのwhere句を指定していますが、その他にも以下のようなキーワードが用意されています。
句 | 概要 |
---|---|
from | データソースと範囲変数を指定 |
where | 絞り込み条件を指定(&& 、|| で区切られたブール式の集合) |
select | クエリーの結果を取得するための形式(型)を定義 |
group | 指定キーでクエリー結果をグループ化 |
orderby | クエリー結果を昇順/降順に並べ替え |
into | join/group/select句の結果を一時変数にセット |
join | 2つのデータソースを結合 |
個別のキーワードについては後節に譲るとして、まずは、基本的なfrom、where、select句について触れておきます。
(1)from句
from句は、問い合わせの対象となるデータソースを表すための句で、以下のような構文で表記します。
【構文】from句
from 範囲変数 in データソース
データソースにはアクセス先のデータソースを指定します。この部分は、オブジェクト配列かもしれませんし、DataSetオブジェクトや、あるいは、XElement配列(要素ノード配列)である場合もあります*19。データソースに指定できる条件は、オブジェクトがIEnumerable<T>/IEnumerableインターフェイス、またはその派生インターフェイスを実装していることだけです。言いかえれば、foreach/For Eachループで処理できるオブジェクトであれば、データソースとして利用できるということです。LINQ to Entitiesの場合には、データソースの部分にはDbSet<T>オブジェクトなどを指定することになるでしょう(2-4-3節(※転載対象外)でコンテキストを定義した際に、登場しましたね)。
- *19 LINQでは、データソースになにが渡されたかによって使用するプロバイダーを決定します。先ほど、使用するプロバイダーをアプリケーション開発者が意識する必要がないと述べたのはこのためです。
範囲変数(Range Variable)とは、やや難しげな言葉ですが、要はデータソースに含まれる個々の要素を一時的に格納するための変数のことです。たとえば、リスト5-25の例であれば、データソースがDbSet<Article>オブジェクト(Articleエンティティの集合)ですので、範囲変数はArticleエンティティを表します。
(2)where句
データソースの内容を絞り込むための条件式を指定します。SQL-SELECT命令のそれとよく似ていますが、条件式の指定にはあくまでC#やVisual Basicの比較演算子を使う点に注意してください(リスト5-25を見ても、比較演算子が=
ではなく==
であることに気付かれるでしょう)。もちろん、&&
、||
などの論理演算子を使うことで複合的な条件式を表すこともできます。
(3)select句
問い合わせの出力形式を決定します。リスト5-25ではselect a.Title
としていますので、(ここでは)Articleエンティティに含まれるTitleプロパティの集合(正確には、IQueryable<String>オブジェクト*20)を返します。
- *20 クエリーを評価し、その結果を列挙するための機能を提供します。
もしも範囲変数(Articleエンティティ)の内容をそのまま返したいという場合には、単にselect a
としても構いません。これは、いわゆるSQL-SELECT命令のSELECT * ...
と同じ意味です。複数のプロパティを取得したい場合には、以下のように記述することもできます。
var article= from a in db.Articles
where a.Url == "https://www.buildinsider.net/web/jquerymobileref"
select new { Title = a.Title, Url = a.Url };
|
ここではArticleエンティティから取り出したTitle/Urlプロパティを、改めて匿名型に詰め直したものを返すようにしているわけです。
いかがですか。こうして説明していくと、難しくも聞こえるかもしれませんが、リスト5-25でやっているのは、「Articleエンティティの集合からUrlプロパティが"https://www.buildinsider.net/web/jquerymobileref"
であるArticleエンティティを取り出し、そのUrlプロパティを取り出している」というだけです。これからも同じようなコードが頻々と登場しますので、繰り返し見て、書いていく中で、コーディングの感覚を身に付けていくことにしましょう。LINQクエリーが決して難しくはないということがお分かりになると思います。
メソッド構文
クエリー式は、じつは、コンパイラーによってメソッド構文に置換された上で実行されます。クエリー式構文とメソッド構文とは個々に独立した構文ではなく、
メソッド構文のシンタックスシュガーがクエリー式構文
なのです。よって、クエリー式構文で書ける内容は、かならずメソッド構文で表現できます。
ただし先ほども述べたように、逆は不可です。つまり、メソッド構文のすべてにクエリー式構文が対応しているわけではありません。たとえば、結果セットの最大/最小値を求めるMax/Minメソッドに対応するクエリー式構文はありません。そのようなケースでは、クエリー式構文とメソッド構文とを混在させることになるでしょう。あるいは、最初からクエリー式構文を利用せずに、メソッド構文で統一することもできます。
では、先ほどのリスト5-25をメソッド構文を使って書き換えてみましょう。
var articles = db.Articles // from句
.Where(a => a.Url == "https://www.buildinsider.net/web/jquerymobileref") // where句
.Select(a => a.Title); // select句
|
メソッド構文とクエリー式構文とが異なる点は、以下のとおりです。
まず、クエリー式構文のfrom句に相当するメソッドはありません。db.Articles
で取得したデータソースからそのままメソッドを呼び出せます(つまり、この部分はクエリー式よりもシンプルです)。
一方、Where/Selectなどのメソッド呼び出しでは、条件/取得式をラムダ式として表します。a => a.Url == '~'
であれば、変数a
が対象のエンティティ、a.Url == '~'
が条件式を表します。変数a
は、クエリー式構文の範囲変数に相当するものです。from句でまとめていたものを、個々のメソッドで指定しなければならないということですので、この部分はクエリー式よりも冗長になります。
遅延実行(deferred execution)
クエリー式構文/メソッド構文に関わらず、LINQを利用する上で知っておかなければならない重要なポイントがあります。それが遅延実行です。
遅延実行については3-2-1項(※転載対象外)でも軽く触れましたが、クエリーの定義によってすぐさまに問い合わせが実行されないという意味です。ただ、組み立てられたクエリーをIQueryable<T>オブジェクトとして返すだけです。そして、結果が必要になったところで初めて、データベースに問い合わせるのです。
メソッド構文で、リスト5-26のようにWhereメソッドを呼び出した後、Selectメソッドを続けて呼び出せたのも、まさにWhereメソッドの時点ではまだクエリーは実行されていなかったからなのです。
リスト5-26では、メソッドをドット演算子で連鎖していましたので、なんとなく見過ごしてしまいそうですが、この性質をよりあからさまに表したのが、以下の例です(実際のコードであえてそのようにする意味はありません)。遅延実行の性質によって、このような条件句の積み上げが自然なコードで可能になるわけです。
var articles = db.Articles;
var where_articles = articles.Where(
a => a.Url == "https://www.buildinsider.net/web/jquerymobileref");
var select_articles = where_articles.Select(
a => new { Title = a.Title, Url = a.Url });
|
遅延実行をその場で確定させたい場合には、3-2-1節でも触れたようにToListメソッドなどを呼び出せば良いのでした。
以上、LINQ to Entities(LINQ)の基本を理解できたところで、(次回の記事で)個別の句(メソッド)について詳らかにしていきます。
※以下では、本稿の前後を合わせて5回分(第16回~第20回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
16. テンプレートのカスタマイズ|テンプレートを決定する方法[Razor]
テンプレートヘルパーでは、特定のデータ型/メタ情報に応じた独自のテンプレートを用意して、独自の出力を生成することもできる。その方法を解説する。書籍転載の16本目(基礎編「4-4-2」「4-4-3」)。
17. モデル単位にテンプレートを決定する - DisplayForModel/EditorForModelメソッド[Razor]
プロパティ単位ではなくモデル単位でHTMLコードを出力するDisplayForModel/EditorForModelメソッドについて解説。また、そのテンプレートを自作する方法を説明する。書籍転載の17本目(基礎編「4-4-4」)。
18. 【現在、表示中】≫ LINQとは?[C#]
LINQ to Entitiesの説明に入る前に、まずはC#の言語機能として組み込まれたクエリ機能「LINQ」について簡単に紹介する。書籍転載の18本目(基礎編「5-3-1」)。
19. LINQ:データの検索条件を指定する - where句[C#]
クエリ後の結果セットをフィルターするためのwhere句/Whereメソッドについて解説する。書籍転載の19本目(基礎編「5-3-2」)。
20. LINQ:データを並べ替える - orderby句[C#]
取得したデータを並べ替えるためのorderby句/OrderByメソッドについて解説する。書籍転載の20本目(基礎編「5-3-3」)。