2018/03/24

[.NET Core]Entity Framework Coreで INNER JOIN / LEFT JOIN を実装する方法


はじめに

Entity Framework Core で INNER JOIN と LEFT JOIN を実装する方法ですが、ちょっと試行錯誤したのですがその方法をご紹介します。

アプリケーションはコンソールアプリケーションで、データベースは PostgreSQL を使用しコードファーストでデータベースを構築しました。

なお、環境は、Mac (macOS High Sierra 10.13.3) で .NET Core SDK 2.1.4、PostgreSQL 10.3 、Visual Studio Code になります。

コードファーストでデータベースを構築する

最初にコードファーストでデータベースを構築する必要があります。

JOIN するために以下のクラスを作成します。ポイントは、Pet クラスが Owner プロパティを持っていることです。

public class Person
{
    public int id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Pet
{
    public int id { get; set; }
    public string Name { get; set; }
    public Person Owner { get; set; }
}

 

そしてデータベースコンテキストクラスを以下のように作成します(抜粋)。SQL ログを出力したいのでその設定も含まれています。

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console;

//(中略)

  public DbSet<Person> Person { get; set; }
  public DbSet<Pet> Pet { get; set; }


  public static readonly LoggerFactory MyLoggerFactory
      = new LoggerFactory(new[]
      {
          new ConsoleLoggerProvider((category, level)
              => category == DbLoggerCategory.Database.Command.Name
              && level == LogLevel.Information, true)
      });

  protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  {
      if (!optionsBuilder.IsConfigured)
      {
          optionsBuilder
              .UseLoggerFactory(MyLoggerFactory)
              .UseNpgsql("Server=localhost;Database=databasename;Username=username;Password=password;");
      }
  }

準備ができたので、以下のコマンドでデータベースを構築します。init 部分は名前が重複しなければなんでもかまいません。

$ dotnet ef migrations add init
$ dotnet ef database update

SQL ログが出力されるのですが興味深いですね。Pet クラスの作成時に Person で Owner 属性を宣言していたのに、実際のテーブルでは Ownerid のみ作成しています。この辺はさすがよくできた O/R マッパーですね。

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (33ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "Person" (
          "id" serial NOT NULL,
          "FirstName" text NULL,
          "LastName" text NULL,
          CONSTRAINT "PK_Person" PRIMARY KEY ("id")
      );

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (37ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "Pet" (
          "id" serial NOT NULL,
          "Name" text NULL,
          "Ownerid" int4 NULL,
          CONSTRAINT "PK_Pet" PRIMARY KEY ("id"),
          CONSTRAINT "FK_Pet_Person_Ownerid" FOREIGN KEY ("Ownerid") REFERENCES "Person" ("id") ON DELETE RESTRICT
      );

 

データの作成

データベースの構築が済んだので、実際にデータをテーブルに作成します。

Program.cs に以下のように記述します。ポイントは、斎藤さんだけペットを飼っていないところです。

static void Main(string[] args)
{
    using (var db = new consoletestContext())
    {
        var yamada = new Person {FirstName = "山田", LastName = "太郎"};
        var sato = new Person {FirstName = "佐藤", LastName = "次郎"};
        var saito = new Person {FirstName = "斎藤", LastName = "三郎"};
        db.Person.Add(yamada);
        db.Person.Add(sato);
        db.Person.Add(saito);

        db.Pet.Add(new Pet { Name = "ポチ", Owner = yamada});
        db.Pet.Add(new Pet { Name = "タマ", Owner = sato});

        db.SaveChanges();
    }

下記コマンドでデータを作成します。

$ dotnet build
$ dotnet run

 

INNER JOIN の実装

C# の join 句を使用することで INNER JOIN を実装することができます。

具体的には、Program.cs に以下のように記述します。 

static void Main(string[] args)
{
    using (var db = new consoletestContext())
    {
        var query = from person in db.Person
                    join pet in db.Pet on person.id equals pet.Owner.id
                    select new
                    {
                        OwnerName = person.FirstName,
                        PetName = pet.Name
                    };

        foreach (var ownerAndPet in query) {
            Console.WriteLine($"{ownerAndPet.PetName} は {ownerAndPet.OwnerName} に飼われてる。");
        }
    }
}

これを実行すると、以下のようなログが出力されます。

$ dotnet build
$ dotnet run
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (20ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT "person"."FirstName" AS "OwnerName", "pet"."Name" AS "PetName"
      FROM "Person" AS "person"
      INNER JOIN "Pet" AS "pet" ON "person"."id" = "pet"."Ownerid"
ポチ は 山田 に飼われてる。
タマ は 佐藤 に飼われてる。

SQL 文で INNER JOIN が使用されていることが分かります。

また、ペットを飼っていない斎藤さんは出力されていないので処理も正しく行われています。

 

LEFT JOIN の実装

LEFT JOIN の実装ですが、グループ結合の結果に対して DefaultEmpty メソッドを使用することで実現できます。

具体的には、Program.cs に以下のように記述します。 

static void Main(string[] args)
{
    using (var db = new consoletestContext())
    {
        var query = from person in db.Person
                    join pet in db.Pet on person.id equals pet.Owner.id into gj
                    from subpet in gj.DefaultIfEmpty()
                    select new
                    {
                        OwnerName = person.FirstName,
                        PetName = subpet.Name ?? String.Empty                             };

        foreach (var v in query) {
            Console.WriteLine($"{v.OwnerName} : {v.PetName}");
        }
    }
}

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

$ dotnet build
$ dotnet run
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (35ms) [Parameters=[@__Empty_0='?'], CommandType='Text', CommandTimeout='30']
      SELECT "person"."FirstName" AS "OwnerName", COALESCE("pet"."Name", @__Empty_0) AS "PetName"
      FROM "Person" AS "person"
      LEFT JOIN "Pet" AS "pet" ON "person"."id" = "pet"."Ownerid"
山田 : ポチ
佐藤 : タマ
斎藤 :

SQL ログに LEFT JOIN が出力されていますし、ペットを飼っていない斎藤さんが出力されているので処理が正しいことがわかります。

 

おわりに

INNER JOIN / LEFT JOIN といったテーブルの結合処理は、データベースを使用すアプリケーションでは必須の知識なので分かってよかったです。

コードファーストでもうまくいくことが分かったこともよかったですね。 

 

参考サイト 


スポンサーリンク


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




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


コメント

コメントを書く



プロフィール

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

    フリーウェア、Webサービス開発のためにEntyで支援を受け付けています。ご支援のほどよろしくお願いいたします。

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


    ブログについて

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


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

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


最近の記事