書籍転載:ASP.NET MVC 5 実践プログラミング
LINQ:取得列を明示的に指定する - select句/SelectManyメソッド[C#]
範囲変数(エンティティ)から特定のプロパティだけを取り出したり、プロパティ値を加工したりするためのselect句/Selectメソッドについて解説。書籍転載の21本目(基礎編「5-3-4」)。
書籍転載について
本コーナーは、秀和システム発行の書籍『ASP.NET MVC 5 実践プログラミング』の中から、特にBuild Insiderの読者に有用だと考えられる項目を編集部が選び、同社の許可を得て転載したものです。
『ASP.NET MVC 5 実践プログラミング』の詳細や購入は秀和システムのサイトや目次ページをご覧ください。
ご注意
本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどはBuild Insiderのそれとは一致しません。あらかじめご了承ください。
5-3-4 取得列を明示的に指定する - select句
select句を利用することで、範囲変数(エンティティ)から特定のプロパティだけを取り出したり*25、プロパティ値を加工したりすることが可能になります。たとえば以下は、Articlesテーブルの内容を、
- Title列を先頭10文字だけ
- Viewcount列を1000単位で
- Released列がtrueであれば「公開中」、さもなければ「公開予定」
のような形式で取得する例です。
- *25 これを射影と言います。
public ActionResult Select()
{
var articles =
from a in db.Articles
orderby a.Published descending
select new ArticleView
{
Title = a.Title.Substring(0, 10),
Viewcount = (int)Math.Ceiling(a.Viewcount / 1000.0),
Released = (a.Released ? " 公開中 " : " 公開予定 ")
};
return View(articles);
}
|
public class ArticleView
{
public string Title { get; set; }
public int Viewcount { get; set; }
public string Released { get; set; }
}
|
特にString/Math/DateTimeオブジェクトのメンバーはよく利用しますので、リファレンスなどできちんとおさえておいてください。同じ内容をメソッド構文で表現すると、以下のようになります。
var articles = db.Articles
.OrderByDescending(a => a.published)
.Select(a => new ArticleView
{
Title = a.Title.Substring(0, 10),
Viewcount = (int)Math.Ceiling(a.Viewcount / 1000.0),
Released = (a.Released ? " 公開中 " : " 公開予定 ")
});
|
【Note】ビューモデル
サンプルで利用しているArticleViewクラスは、ビューとして表示/操作すべきデータを表します。このようなモデルのことをビューモデルと呼びます。
これまではEntity Frameworkで利用しているエンティティをそのままビューモデルとしても利用してきましたが、それはたまたまであるにすぎません。この例であれば、Selectメソッドによって、Articleエンティティから一部のプロパティを取り出していますが、複数のエンティティを組み合わせることもあるでしょうし、そもそもエンティティとは対応関係にない項目をビューモデルとして表すようなこともあるでしょう。
モデルと一口に言っても、それが指すものはひとつではないことを覚えておいてください。
結果セットをフラットにする - SelectManyメソッド
Selectメソッドによく似たメソッドとして、SelectManyメソッドがあります。SelectManyメソッドはエンティティから特定のプロパティを取り出すという意味では、Selectメソッドと同じですが、最後の結果をフラット化(平坦化)する点が異なります。
説明だけだとイメージしにくいと思いますので、具体的な例を示します。以下は、特定の記事情報に関連付いたコメント情報を列挙する例です。まずは、Selectメソッドを使って表してみます。
public ActionResult SelectMany()
{
// Reference カテゴリーに属する記事のコメントを取得
var comments = db.Articles
.Where(a => a.Category == CategoryEnum.Reference)
.Select(a => a.Comments);
return View(comments);
}
|
この場合、変数comments
はIEnumerable<IEnumerable<Comment>>
なので、これを取得するには、以下のような二重ループが必要となります。
@model IEnumerable<ICollection<MvcModel.Models.Comment>>
... 中略 ...
@foreach (var cs in Model) {
foreach (var c in cs) {
<li>@c.Body(@c.Name)</li>
}
}
|
しかし、SelectManyメソッドを利用することで、これを展開できます。
public ActionResult SelectMany2()
{
var comments = db.Articles
.Where(a => a.Category == CategoryEnum.Reference)
.SelectMany(a => a.Comments);
return View(comments);
}
|
この場合、戻り値はIEnumerable<Comment>
となりますので、以下のようなループで情報を取得できます(これがフラット化の意味です)。
@foreach (var c in Model) {
<li>@c.Body(@c.Name)</li>
}
|
リスト5-39をクエリー式構文で表すと、以下のようになります。from...from...select
という構文で書かれた場合、最後のselect句は暗黙的にSelectManyメソッドに変換されるわけです。
public ActionResult SelectMany3()
{
var comments =
from a in db.Articles
where a.Category == CategoryEnum.Reference
from c in a.Comments
select c;
return View(comments);
}
|
- *26 対応するビュースクリプトSelectMany3.cshtmlは、配布サンプルも参照してください。
応用として、指定されたカテゴリーに属する記事名と、配下のコメントを列挙する例も示しておきます(上がメソッド構文、下がクエリー式構文)。
public ActionResult SelectMany4()
{
var comments = db.Articles
.Where(a => a.Category == CategoryEnum.Reference)
.SelectMany(a => a.Comments
.Select(c => new ArticleCommentView { Title = a.Title, Body = c.Body })
);
return View(comments);
}
|
public ActionResult SelectMany4()
{
var comments =
from a in db.Articles
where a.Category == CategoryEnum.Reference
from c in a.Comments
select new ArticleCommentView { Title = a.Title, Body = c.Body };
return View(comments);
}
|
- *27 対応するビューモデルArticleCommentModel.cs、ビュースクリプトSelectMany4.cshtmlは、配布サンプルを参照してください。
※以下では、本稿の前後を合わせて5回分(第19回~第23回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
19. LINQ:データの検索条件を指定する - where句[C#]
クエリ後の結果セットをフィルターするためのwhere句/Whereメソッドについて解説する。書籍転載の19本目(基礎編「5-3-2」)。
20. LINQ:データを並べ替える - orderby句[C#]
取得したデータを並べ替えるためのorderby句/OrderByメソッドについて解説する。書籍転載の20本目(基礎編「5-3-3」)。
21. 【現在、表示中】≫ LINQ:取得列を明示的に指定する - select句/SelectManyメソッド[C#]
範囲変数(エンティティ)から特定のプロパティだけを取り出したり、プロパティ値を加工したりするためのselect句/Selectメソッドについて解説。書籍転載の21本目(基礎編「5-3-4」)。
22. LINQ:重複のないデータを取得する - Distinctメソッド[C#]
クエリ後の結果セットから重複したデータを除去するためのDistinctメソッドについて解説する。書籍転載の22本目(基礎編「5-3-5」)。
23. LINQ:特定範囲のデータだけを取得する - Skip/Takeメソッド[C#]
指定された件数だけデータを読み飛ばすためのSkipメソッドと、指定された件数のデータだけを取得するためのTakeメソッドについて解説。書籍転載の23本目(基礎編「5-3-6」)。