2014/01/03

[Entity Framework] LINQ で JOIN 等を使わずに複数テーブルからデータを取得する


Entity Framework では、複数のエンティティが 1:N の関係の場合、親エンティティに子エンティティのコレクションを持ち、子エンティティに親エンティティのキーと親エンティティを持つことにより、SQL で行う 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 クラスのインスタンスを設定しているところがポイントです。他の注意点は、各クラスのフィールドにコメントしてあります。

・Program.cs

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

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

      using (var context = new BlogContext())
      {
        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);

        context.SaveChanges();

        var posts = from t in context.Posts
              orderby t.PostId
              select t;

        foreach (var post in posts)
        {
          Console.WriteLine("[Post]");
          Console.WriteLine(post.PostId.ToString());
          Console.WriteLine(post.Author);
          Console.WriteLine(post.Title); 
          Console.WriteLine(post.Body);
          Console.WriteLine(post.Posted.ToLongDateString());

          foreach (var comment in post.Comments)
          {
            Console.WriteLine("[Comment]");
            Console.WriteLine(comment.CommentId.ToString());
            Console.WriteLine(comment.Author);
            Console.WriteLine(comment.Body);
            Console.WriteLine(comment.Posted.ToLongDateString());
          }
        }

        Console.ReadKey();
      }
    }
  }
}

実行結果は以下のようになります。

[Post]
1
山田太郎
いい天気
こんにちは。いい天気です。
2014年1月4日
[Comment]
1
山田花子
こんにちは
2014年1月4日
[Comment]
2
佐藤次郎
よろしくお願いします。
2014年1月4日

JOIN を記述していないのに、Posts と Comments の複数テーブルからデータを取得できていることが分かります。

まとめ

Entity Framework では、エンティティの関連を設定し、ルールに従ってデータ更新すれば、JOIN を使わなくても複数テーブルからデータを取得することができることが分かりました。

若干、慣れが必要ですが、コードがシンプルになるのはいいですね。

この方法だと、パフォーマンスがあまりよくないようです。パフォーマンスを改善する方法を関連エントリーに載せたのでご参照ください。

関連エントリー

参考サイト


スポンサーリンク


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




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


コメント

コメントを書く



プロフィール

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

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


    ブログについて

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

    現在、Enty で支援を受け付けています。もしよければご支援ください。



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

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