読者です 読者をやめる 読者になる 読者になる

dapper dot net - 1対多のテーブルをコレクションプロパティにマッピングする

いつものごとくタイトルが微妙な気もしますが、やりたいことは「DBに注文テーブルと注文明細テーブルがあって、注文明細のコレクションを持っている注文にマッピングしたい」といった感じのことです。

てことでマッピングするクラスです。

// 注文
class Order {
    public int Id { get; set; }
    public string Seat { get; set; }  // テーブル番号とかカウンター番号のこと

    // 注文明細のコレクション
    public List<OrderDetail> Details { get; set; }
}

// 注文明細
class OrderDetail {
    public int Id { get; set; }
    public int OrderId { get; set; }
    public string Menu { get; set; }  // 注文したメニューの名前
}

Order.DetailsプロパティにOrderDetailを入れてOrderのコレクションを取得したいと思います。他にいい方法があるのかわかりませんが、とりあえず試したサンプルコードを残しておきます。

テストデータを用意します。

connection.Execute(@"
-- 注文テーブルを作成
create table #Order(Id int, Seat nvarchar(50));
-- 注文明細テーブルを作成
create table #OrderDetail(Id int, OrderId int, Menu nvarchar(50));

insert into #Order(Id, Seat)
values(1, N'1番テーブル'), (2, N'2番カウンター');

-- 1番テーブルのお客さんが純けい、ねぎま、しろを注文
insert into #OrderDetail(Id, OrderId, Menu)
values(1, 1, N'純けい'), (2, 1, N'ねぎま'), (3, 1, N'しろ');

-- 2番カウンターさんが串かつとねぎまを注文
insert into #OrderDetail(Id, OrderId, Menu)
values(4, 2, N'串かつ'), (5, 2, N'ねぎま');");
}

純けいを食べたくなりましたが、データを取り出します。

var orders = connection.Query<Order, OrderDetail, Tuple<Order, OrderDetail>>(@"
select *
from #Order
  inner join #OrderDetail
      on #Order.Id = #OrderDetail.OrderId;",
    // 一旦Tupleで受ける
    (order, orderDetail) => Tuple.Create(order, orderDetail))
    // Order.Idでグルーピング
    .GroupBy(
        tuple => tuple.Item1.Id,
        (key, tuples) => {
            var order = tuples.First().Item1;
            // OrderDetailのコレクションを設定
            order.Details = tuples.Select(tuple => tuple.Item2).ToList();
            return order;
        });

foreach (var order in orders) {
    Console.WriteLine(order.Seat);
    order.Details.ForEach(detail => Console.WriteLine("\t" + detail.Menu));
}
/*
1番テーブル
   純けい
   ねぎま
   しろ
2番カウンター
   串かつ
   ねぎま
*/

一旦Tupleで各レコードを受けてOrder.Idでグルーピングしてから、Order.DetailsにOrderDetailのコレクションを設定しています。dapper dot netではなくてLINQのサンプルになってしまった気もしますが以上です。

純けい食べたいです。