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?
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.
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.
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. :)
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 });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With