Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to perform Join between multiple tables in LINQ lambda

I am trying to perform a Join between multiple tables in LINQ. I have the following classes:

Product {Id, ProdName, ProdQty}  Category {Id, CatName}  ProductCategory{ProdId, CatId} //association table 

And I use the following code (where product, category and productcategory are instances of the above classes):

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}); 

With this code I obtain an object from the following class:

QueryClass { productproductcategory, category} 

Where producproductcategory is of type:

ProductProductCategoryClass {product, productcategory} 

I do not understand where the joined "table" is, I was expecting a single class that contains all the properties from the involved classes.

My aim is to populate another object with some properties resulting from the query:

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

how can I achieve this goal?

like image 849
CiccioMiami Avatar asked Mar 15 '12 13:03

CiccioMiami


People also ask

Can we do Joins in LINQ?

In a LINQ query expression, join operations are performed on object collections. Object collections cannot be "joined" in exactly the same way as two relational tables. In LINQ, explicit join clauses are only required when two source sequences are not tied by any relationship.

Can I join a table to a list using LINQ?

When the query actually runs, LINQ should be able to create a temp table or table variable with the data from the local list and then join on that. This is a feature that should absolutely be included in the framework.


2 Answers

For joins, I strongly prefer query-syntax for all the details that are happily hidden (not the least of which are the transparent identifiers involved with the intermediate projections along the way that are apparent in the dot-syntax equivalent). However, you asked regarding Lambdas which I think you have everything you need - you just need to put it all together.

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     }); 

If you need to, you can save the join into a local variable and reuse it later, however lacking other details to the contrary, I see no reason to introduce the local variable.

Also, you could throw the Select into the last lambda of the second Join (again, provided there are no other operations that depend on the join results) which would give:

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     }); 

...and making a last attempt to sell you on query syntax, this would look like this:

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     }; 

Your hands may be tied on whether query-syntax is available. I know some shops have such mandates - often based on the notion that query-syntax is somewhat more limited than dot-syntax. There are other reasons, like "why should I learn a second syntax if I can do everything and more in dot-syntax?" As this last part shows - there are details that query-syntax hides that can make it well worth embracing with the improvement to readability it brings: all those intermediate projections and identifiers you have to cook-up are happily not front-and-center-stage in the query-syntax version - they are background fluff. Off my soap-box now - anyhow, thanks for the question. :)

like image 179
devgeezer Avatar answered Sep 17 '22 13:09

devgeezer


What you've seen is what you get - and it's exactly what you asked for, here:

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

That's a lambda expression returning an anonymous type with those two properties.

In your CategorizedProducts, you just need to go via those properties:

CategorizedProducts catProducts = query.Select(       m => new {               ProdId = m.productproductcategory.product.Id,               CatId = m.category.CatId,               // other assignments             }); 
like image 39
Jon Skeet Avatar answered Sep 19 '22 13:09

Jon Skeet