LINQラムダで複数のテーブル間の結合を実行する方法


89

LINQで複数のテーブル間結合を実行しようとしています。次のクラスがあります。

Product {Id, ProdName, ProdQty}

Category {Id, CatName}

ProductCategory{ProdId, CatId} //association table

そして、私は次のコードを使用します(ここでproductcategoryおよびproductcategoryは上記のクラスのインスタンスです)。

var query = product.Join(productcategory, p => p.Id, pc => pc.ProdID, (p, pc) => new {product = p, productcategory = pc})
                   .Join(category, ppc => ppc.productcategory.CatId, c => c.Id, (ppc, c) => new { productproductcategory = ppc, category = c});

このコードを使用して、次のクラスからオブジェクトを取得します。

QueryClass { productproductcategory, category}

producproductcategoryが次のタイプの場合:

ProductProductCategoryClass {product, productcategory}

結合された「テーブル」の場所がわかりません。関係するクラスのすべてのプロパティを含む単一のクラスを期待していました。

私の目的は、クエリの結果として得られたいくつかのプロパティを別のオブジェクトに入力することです。

CategorizedProducts catProducts = query.Select(m => new { m.ProdId = ???, m.CatId = ???, //other assignments });

どうすればこの目標を達成できますか?


理解できませんでした...なぜm.ProdId = ??? prodId = m.ProdIdの代わりに?
アドリアーノRepetti

ProdIdをナビゲートして取得する方法が事前にわからないため
CiccioMiami

回答:


179

結合の場合、私は喜んで隠されているすべての詳細に対してクエリ構文を強く推奨します(ドット構文構文で明らかである方法に沿った中間の投影に関連する透明な識別子はその少なくともです)。ただし、ラムダスについては、必要なものはすべて揃っていると私は思っています。すべてを組み合わせるだけです。

var categorizedProducts = product
    .Join(productcategory, p => p.Id, pc => pc.ProdId, (p, pc) => new { p, pc })
    .Join(category, ppc => ppc.pc.CatId, c => c.Id, (ppc, c) => new { ppc, c })
    .Select(m => new { 
        ProdId = m.ppc.p.Id, // or m.ppc.pc.ProdId
        CatId = m.c.CatId
        // other assignments
    });

必要な場合は、結合をローカル変数に保存して後で再利用できますが、逆に他の詳細がないため、ローカル変数を導入する理由はわかりません。

また、Select2番目の最後のラムダにをスローすることもできますJoin(ここでも、結合の結果に依存する他の操作がない場合)。

var categorizedProducts = product
    .Join(productcategory, p => p.Id, pc => pc.ProdId, (p, pc) => new { p, pc })
    .Join(category, ppc => ppc.pc.CatId, c => c.Id, (ppc, c) => new {
        ProdId = ppc.p.Id, // or ppc.pc.ProdId
        CatId = c.CatId
        // other assignments
    });

...そしてクエリ構文であなたを売ろうとする最後の試みをすると、これは次のようになります:

var categorizedProducts =
    from p in product
    join pc in productcategory on p.Id equals pc.ProdId
    join c in category on pc.CatId equals c.Id
    select new {
        ProdId = p.Id, // or pc.ProdId
        CatId = c.CatId
        // other assignments
    };

クエリ構文が利用できるかどうかにあなたの手が縛られているかもしれません。私はいくつかの店がそのような義務を持っていることを知っています-多くの場合、クエリ構文はドット構文よりもいくらか制限されているという考えに基づいています。「ドット構文ですべてを実行できるのに、なぜ2番目の構文を学ぶ必要があるのか​​」など、他の理由もあります。この最後の部分が示すように、クエリ構文が非表示になり、読みやすさの向上を受け入れる価値がある可能性があります。クックアップする必要があるすべての中間の投影と識別子は、前面と中央ではありません。クエリ構文バージョンの段階-それらは背景の綿毛です。私の石鹸箱から今-とにかく、質問をありがとう。:)


3
おかげであなたのソリューションはより完全です。場合によってはクエリ構文がより明確であることに同意しますが、ご想像どおり、ラムダを使用するように求められました。さらに、私はこれを6つのテーブルに結合する必要があり、この場合のドット表記はよりきちんとしています
CiccioMiami

@devgeezer JOINステートメントに追加条件が必要な場合はどうなりますか?どうすればよいですか?たとえば、join pc in productcategory on p.Id equals pc.ProdId行にを追加する必要がありand p.Id == 1ます。
ハランベ攻撃ヘリコプター2016

それはp.Id == 1結合基準よりもwhere-filterのほうが多いので、あなたが望むのは疑わしいようです。一般に、複数の基準で結合を行う方法は、匿名型を使用することですjoin pc in productcategory on new { Id = p.Id, Other = p.Other } equals new { Id = pc.ProdId, Other = pc.Other }。これはLinq-to-Objectsで機能し、データベースクエリでも同様に機能すると思います。データベースを使用すると、外部キーを適切に定義し、関連プロパティを通じて関連データにアクセスすることで、複雑な結合クエリを回避できる場合があります。
devgeezer 2016年

きれいな解決策をありがとう。
Thomas.Benz 2017年

あなたの例では:ドット構文では、ppc ppc.pは匿名型ですよね?クエリ構文で、最後の選択で使用したp.idはまだ製品オブジェクトですか?したがって、複数のテーブルを結合して、min minbyのような最終的に返されるシェマで操作を実行すると、クエリ構文がより簡単になりますか?
CDrosos

12

あなたが見たものはあなたが得るものです-そしてそれはまさにあなたが求めたものです、ここ:

(ppc, c) => new { productproductcategory = ppc, category = c}

これは、これら2つのプロパティを持つ匿名型を返すラムダ式です。

CategorizedProductsでは、これらのプロパティを経由するだけです。

CategorizedProducts catProducts = query.Select(
      m => new { 
             ProdId = m.productproductcategory.product.Id, 
             CatId = m.category.CatId, 
             // other assignments 
           });

ありがとう。匿名クラスについての議論を理解しましたが、そのプロパティにはクエリを満たすクラスオブジェクトのみが含まれていますか?また、2つの結合を実行した後はどうなりますか?productproductcategory.productはカテゴリと結合されていませんか?
CiccioMiami

@CiccioMiami:ええと、プロパティはオブジェクトへの参照です。「結合されていない」という意味が明確ではありません。クエリから取得したくない情報のうち、取得したいものはどれですか。
Jon Skeet

最初の結合で、製品と製品カテゴリの結合が得られます。2つ目は、productcategory(結合された製品)とカテゴリの結合を取得します。つまり、複数結合に関する情報は単にproductproductcategoryに含まれています。これは、製品(およびカテゴリ)が製品カテゴリと結合されることを意味します。
CiccioMiami

1
@CiccioMiami:申し訳ありませんが、私はあなたをフォローしていません-しかし、結合を指定すると、それが実行されます。あなたが持ってしようとした私の答えのコードを使用していますか?それはあなたが望むことをしませんか?
Jon Skeet

申し訳ありませんが、私はあなたのコードに行きたかったです。の割り当ては問題なくCatId 機能します。以下のためにProdIdそれがあるべきm.productproductcategory.product.IdOR m.productproductcategory.productcategory.ProdId。2つの割り当ては異なります。1つ目は製品(と結合productcategory)で、2つ目はproductcategory両方productとで結合されcategoryます。あなたは私の推論に従いますか?
CiccioMiami

4

私のプロジェクトからこのサンプルコードを見てください

public static IList<Letter> GetDepartmentLettersLinq(int departmentId)
{
    IEnumerable<Letter> allDepartmentLetters =
        from allLetter in LetterService.GetAllLetters()
        join allUser in UserService.GetAllUsers() on allLetter.EmployeeID equals allUser.ID into usersGroup
        from user in usersGroup.DefaultIfEmpty()// here is the tricky part
        join allDepartment in DepartmentService.GetAllDepartments() on user.DepartmentID equals allDepartment.ID
        where allDepartment.ID == departmentId
        select allLetter;

    return allDepartmentLetters.ToArray();
}

このコードでは、3つのテーブルを結合し、where句から結合条件を吐き出しました

注:サービスクラスは、データベース操作をワープ(カプセル化)するだけです


2
 public ActionResult Index()
    {
        List<CustomerOrder_Result> obj = new List<CustomerOrder_Result>();

       var  orderlist = (from a in db.OrderMasters
                         join b in db.Customers on a.CustomerId equals b.Id
                         join c in db.CustomerAddresses on b.Id equals c.CustomerId
                         where a.Status == "Pending"
                         select new
                         {
                             Customername = b.Customername,
                             Phone = b.Phone,
                             OrderId = a.OrderId,
                             OrderDate = a.OrderDate,
                             NoOfItems = a.NoOfItems,
                             Order_amt = a.Order_amt,
                             dis_amt = a.Dis_amt,
                             net_amt = a.Net_amt,
                             status=a.Status,  
                             address = c.address,
                             City = c.City,
                             State = c.State,
                             Pin = c.Pin

                         }) ;
       foreach (var item in orderlist)
       {

           CustomerOrder_Result clr = new CustomerOrder_Result();
           clr.Customername=item.Customername;
           clr.Phone = item.Phone;
           clr.OrderId = item.OrderId;
           clr.OrderDate = item.OrderDate;
           clr.NoOfItems = item.NoOfItems;
           clr.Order_amt = item.Order_amt;
           clr.net_amt = item.net_amt;
           clr.address = item.address;
           clr.City = item.City;
           clr.State = item.State;
           clr.Pin = item.Pin;
           clr.status = item.status;

           obj.Add(clr);



       }

1
このコードスニペットは問題を解決する可能性がありますが、説明を含めると、投稿の品質を向上させるのに役立ちます。あなたは将来の読者のための質問に答えていることを覚えておいてください、そしてそれらの人々はあなたのコード提案の理由を知らないかもしれません。
Rob Lang博士

0
var query = from a in d.tbl_Usuarios
                    from b in d.tblComidaPreferidas
                    from c in d.tblLugarNacimientoes
                    select new
                    {
                        _nombre = a.Nombre,
                        _comida = b.ComidaPreferida,
                        _lNacimiento = c.Ciudad
                    };
        foreach (var i in query)
        {
            Console.WriteLine($"{i._nombre } le gusta {i._comida} y nació en {i._lNacimiento}");
        }

単純にそれだけですが、一部の人々が言っ​​たようにラムダexpの方が優れています。
Alex Martinez

0

それはしばらくの間ですが、私の答えは誰かを助けるかもしれません:

関係をすでに適切に定義している場合は、これを使用できます。

        var res = query.Products.Select(m => new
        {
            productID = product.Id,
            categoryID = m.ProductCategory.Select(s => s.Category.ID).ToList(),
        }).ToList();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.