[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さんをフォロー
これはクソ記事
投稿: | 2022/04/15 11:02
こちらのコピーでしかない
https://docs.microsoft.com/ja-jp/dotnet/csharp/linq/perform-left-outer-joins
投稿: | 2022/04/15 11:02