Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
書籍転載:ASP.NET MVC 5 実践プログラミング

書籍転載:ASP.NET MVC 5 実践プログラミング

LINQ:取得列を明示的に指定する - select句/SelectManyメソッド[C#]

2014年11月25日

範囲変数(エンティティ)から特定のプロパティだけを取り出したり、プロパティ値を加工したりするための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 これを射影と言います。
C#
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);
}
リスト5-34 Controllers/LinqController.cs
C#
public class ArticleView
{
  public string Title { get; set; }
  public int Viewcount { get; set; }
  public string Released { get; set; }
}
リスト5-35 Models/ArticleView.cs
図5-16 それぞれ加工した列の値を表示

 特にString/Math/DateTimeオブジェクトのメンバーはよく利用しますので、リファレンスなどできちんとおさえておいてください。同じ内容をメソッド構文で表現すると、以下のようになります。

C#
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 ? " 公開中 " : " 公開予定 ")
  });
リスト5-36 Controllers/LinqController.cs

【Note】ビューモデル

 サンプルで利用しているArticleViewクラスは、ビューとして表示/操作すべきデータを表します。このようなモデルのことをビューモデルと呼びます。

 これまではEntity Frameworkで利用しているエンティティをそのままビューモデルとしても利用してきましたが、それはたまたまであるにすぎません。この例であれば、Selectメソッドによって、Articleエンティティから一部のプロパティを取り出していますが、複数のエンティティを組み合わせることもあるでしょうし、そもそもエンティティとは対応関係にない項目をビューモデルとして表すようなこともあるでしょう。

 モデルと一口に言っても、それが指すものはひとつではないことを覚えておいてください。

結果セットをフラットにする - SelectManyメソッド

 Selectメソッドによく似たメソッドとして、SelectManyメソッドがあります。SelectManyメソッドはエンティティから特定のプロパティを取り出すという意味では、Selectメソッドと同じですが、最後の結果をフラット化(平坦化)する点が異なります。

 説明だけだとイメージしにくいと思いますので、具体的な例を示します。以下は、特定の記事情報に関連付いたコメント情報を列挙する例です。まずは、Selectメソッドを使って表してみます。

C#
public ActionResult SelectMany()
{
  // Reference カテゴリーに属する記事のコメントを取得
  var comments = db.Articles
    .Where(a => a.Category == CategoryEnum.Reference)
    .Select(a => a.Comments);

  return View(comments);
}
リスト5-37 Controllers/LinqController.cs

 この場合、変数commentsIEnumerable<IEnumerable<Comment>>なので、これを取得するには、以下のような二重ループが必要となります。

Razor
@model IEnumerable<ICollection<MvcModel.Models.Comment>>
... 中略 ...
@foreach (var cs in Model) {
  foreach (var c in cs) {
    <li>@c.Body@c.Name</li>
  }
}
リスト5-38 Views/Linq/SelectMany.cshtml

 しかし、SelectManyメソッドを利用することで、これを展開できます。

C#
public ActionResult SelectMany2()
{
  var comments = db.Articles
   .Where(a => a.Category == CategoryEnum.Reference)
   .SelectMany(a => a.Comments);

  return View(comments);
}
リスト5-39 Controllers/LinqController.cs

 この場合、戻り値はIEnumerable<Comment>となりますので、以下のようなループで情報を取得できます(これがフラット化の意味です)。

Razor
@foreach (var c in Model) {
  <li>@c.Body@c.Name</li>
}
リスト5-40 Views/Linq/SelectMany2.cshtml

 リスト5-39をクエリー式構文で表すと、以下のようになります。from...from...selectという構文で書かれた場合、最後のselect句は暗黙的にSelectManyメソッドに変換されるわけです。

C#
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);
}
リスト5-41 Controllers/LinqController.cs *26
  • *26 対応するビュースクリプトSelectMany3.cshtmlは、配布サンプルも参照してください。

 応用として、指定されたカテゴリーに属する記事名と、配下のコメントを列挙する例も示しておきます(上がメソッド構文、下がクエリー式構文)。

C#
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);
}
C#
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);
}
リスト5-42 Controllers/LinqController.cs(上:メソッド構文、下:クエリー式構文)*27
  • *27 対応するビューモデルArticleCommentModel.cs、ビュースクリプトSelectMany4.cshtmlは、配布サンプルを参照してください。

※以下では、本稿の前後を合わせて5回分(第19回~第23回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

書籍転載:ASP.NET MVC 5 実践プログラミング
19. LINQ:データの検索条件を指定する - where句[C#]

クエリ後の結果セットをフィルターするためのwhere句/Whereメソッドについて解説する。書籍転載の19本目(基礎編「5-3-2」)。

書籍転載:ASP.NET MVC 5 実践プログラミング
20. LINQ:データを並べ替える - orderby句[C#]

取得したデータを並べ替えるためのorderby句/OrderByメソッドについて解説する。書籍転載の20本目(基礎編「5-3-3」)。

書籍転載:ASP.NET MVC 5 実践プログラミング
21. 【現在、表示中】≫ LINQ:取得列を明示的に指定する - select句/SelectManyメソッド[C#]

範囲変数(エンティティ)から特定のプロパティだけを取り出したり、プロパティ値を加工したりするためのselect句/Selectメソッドについて解説。書籍転載の21本目(基礎編「5-3-4」)。

書籍転載:ASP.NET MVC 5 実践プログラミング
22. LINQ:重複のないデータを取得する - Distinctメソッド[C#]

クエリ後の結果セットから重複したデータを除去するためのDistinctメソッドについて解説する。書籍転載の22本目(基礎編「5-3-5」)。

書籍転載:ASP.NET MVC 5 実践プログラミング
23. LINQ:特定範囲のデータだけを取得する - Skip/Takeメソッド[C#]

指定された件数だけデータを読み飛ばすためのSkipメソッドと、指定された件数のデータだけを取得するためのTakeメソッドについて解説。書籍転載の23本目(基礎編「5-3-6」)。

サイトからのお知らせ

Twitterでつぶやこう!