2014/01/04

[Entity Framework] LINQ で LEFT JOIN 句を使用して複数テーブルからデータを取得する


前回のエントリー では、(INNER) JOIN 句を使用して複数テーブルからデータを取得しましたが、今回は、LEFT JOIN 句を使用する方法をご紹介します。

データベース定義などは前回の通りですが、念のため掲載しておきます。

前提条件

以下の前提条件で動作検証しています。

  • Visual Studio Express 2013 for Windows Desktop
  • Entity Framework 6.0.2
  • SQL Server 2012 Express
  • コードファーストのコンソールアプリケーション

Entity Framework 関連

作成するデータベースはブログを想定しています。ブログのポスト(投稿)とコメントを管理します。ポストは Post クラス、コメントは Comment クラスになります。

・Post.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EFRelation
{
  public class Post
  {
    public int PostId { get; set; }

    public string Author { get; set; }

    public string Title { get; set; }

    public string Body { get; set; }

    public DateTime Posted { get; set; }

    //コメント一覧 
    public virtual ICollection<Comment> Comments { get; set; }
  }
}

・Comment.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EFRelation
{
  public class Comment
  {
    public int CommentId { get; set; }

    public string Author { get; set; }

    public string Body { get; set; }

    public DateTime Posted { get; set; }

    //外部キー
    //Post を設定すると自動的に更新される
    public int PostId { get; set; }

    //親フィールド、テーブルには作成されないが
    //このフィールドの更新が必要
    public virtual Post Post { get; set; }
  }
}

テーブルの定義を行うコンテキストクラスを作成する必要があります。

・BlogContext.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;

namespace EFRelation
{
  class BlogContext : DbContext
  {
    public DbSet<Post> Posts { get; set; }

    public DbSet<Comment> Comments { get; set; }
  }
}

App.config にデータベースの接続文字列を追加します。コンテキストクラスと同じ名前である必要があるので注意してください。

・App.config

<connectionStrings>
  <add name="BlogContext"
       connectionString="Data Source=(local)\SQLEXPRESS;Initial Catalog=Blog;Integrated Security=True;"
       providerName="System.Data.SqlClient" />
</connectionStrings>

データ更新・参照プログラム

データを更新・参照するプログラムは以下のようになります。

コメント追加時に、Post クラスのインスタンスを設定しているところがポイントです。

LINQ クエリ式にて、 JOIN 句 の部分で DefaultIfEmpty メソッドを使用することにより LEFT JOIN の外部結合を実現しています。

なお、実際にどのような SQL が発行されたか分かるように、SQL ログをデバッグ出力しています。

・Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
using System.Diagnostics;

namespace EFRelation
{
  class Program
  {
    static void Main(string[] args)
    {
      //データベース初期化
      Database.SetInitializer(new DropCreateDatabaseIfModelChanges<BlogContext>());

      using (var context = new BlogContext())
      {
        //SQLログ設定
        context.Database.Log = Logger;

        var post1 = new Post(){
                Author="山田太郎",
                Title="いい天気",
                Body="こんにちは。いい天気です。",
                Posted = DateTime.Now};

        context.Posts.Add(post1);

        var comment1 = new Comment() {
                Author = "山田花子",
                Body ="こんにちは",
                Posted = DateTime.Now,
                Post = post1};

        context.Comments.Add(comment1);

        var comment2 = new Comment() {
                Author = "佐藤次郎",
                Body = "よろしくお願いします。",
                Posted = DateTime.Now,
                Post = post1};

        context.Comments.Add(comment2);

        //コメントなしの投稿
        var post2 = new Post() {
                Author = "田中三郎",
                Title = "散歩",
                Body = "いい景色です",
                Posted = DateTime.Now};

        context.Posts.Add(post2);

        context.SaveChanges();

        var posts = from p in context.Posts
              join c in context.Comments
              on p.PostId equals c.PostId into gj
              from x in gj.DefaultIfEmpty()
              orderby p.PostId
              select new
              {
                p.PostId,
                PostAuthor = p.Author, //AS 句に該当 
                PostTitle = p.Title,
                PostBody = p.Body,
                CommentAuthor = (x.Author == null ? string.Empty : x.Author),
                CommentBody = (x.Body == null ? string.Empty : x.Body)
              };

        int id = 0;

        foreach (var post in posts)
        {
          if (id != post.PostId)
          {
            Console.WriteLine("[Post]");
            Console.WriteLine(post.PostAuthor);
            Console.WriteLine(post.PostTitle);
            Console.WriteLine(post.PostBody);
            id = post.PostId;
          }

          Console.WriteLine("[Comment]");
          Console.WriteLine(post.CommentAuthor);
          Console.WriteLine(post.CommentBody);

        }

        Console.ReadKey();
      }
    }

    private static void Logger(string message)
    {
      Debug.Write(message);
    }

  }
}

コンソールの出力結果は以下のようになります。投稿のみのデータも、コメントなしでちゃんと出力されていますね。

[Post]
山田太郎
いい天気
こんにちは。いい天気です。
[Comment]
山田花子
こんにちは
[Comment]
佐藤次郎
よろしくお願いします。
[Post]
田中三郎
散歩
いい景色です
[Comment]

以下は、SQL ログで出力された LEFT JOIN の部分です。問題なく LEFT JOIN で出力されていますね。

SELECT
    [Project1].[PostId] AS [PostId],
    [Project1].[Author] AS [Author],
    [Project1].[Title] AS [Title],
    [Project1].[Body] AS [Body],
    [Project1].[C1] AS [C1],
    [Project1].[C2] AS [C2]
    FROM ( SELECT
        [Extent1].[PostId] AS [PostId],
        [Extent1].[Author] AS [Author],
        [Extent1].[Title] AS [Title],
        [Extent1].[Body] AS [Body],
        CASE WHEN ([Extent2].[Author] IS NULL) THEN @p__linq__0 ELSE [Extent2].[Author] END AS [C1],
        CASE WHEN ([Extent2].[Body] IS NULL) THEN @p__linq__1 ELSE [Extent2].[Body] END AS [C2]
        FROM  [dbo].[Posts] AS [Extent1]
        LEFT OUTER JOIN [dbo].[Comments] AS [Extent2] ON [Extent1].[PostId] = [Extent2].[PostId]
    )  AS [Project1]
    ORDER BY [Project1].[PostId] ASC
-- p__linq__0: '' (Type = String, Size = 4000)
-- p__linq__1: '' (Type = String, Size = 4000)
-- Executing at 2014/01/04 11:20:13 +09:00
-- Completed in 4 ms with result: SqlDataReader

まとめ

Entity Framework で LEFT JOIN 句を使用する方法を見てきましたが、いかがでしたでしょうか。

個人的には、ちょっと分かりにくい構文になっているなと感じています。

まあ、これは慣れですかね。

関連エントリー

参考サイト


スポンサーリンク


このエントリーをはてなブックマークに追加




Twitter ではブログにはない、いろんな情報を発信しています。


コメント

コメントを書く



プロフィール

  • 名前:fnya
    経歴:
    SE としての経験は15年以上。様々な言語と環境で業務系システム開発を行い、セキュリティ対策などもしていました。現在は趣味SE。

    Twitter では、ブログでは取り上げない情報も公開しています。


    ブログについて

    このブログは、IT、スマートフォン、タブレット、システム開発などに関するさまざまな話題を取り上げたり、雑感などをつづっています。


    >>ブログ詳細
    >>自作ツール
    >>運営サイト
    >>Windows 10 まとめ

    Twitter のフォローはこちらから Facebook ページはこちら Google+ページはこちら RSSフィードのご登録はこちらから