0

I have a class that looks like

public class ProductCategory {
  public int ProductCategoryId { get; set; }
  public string ProductCategoryName { get; set; }
  public List<Product> Products { get; set; }

and product has a few properties such as name, price etc.

I want a linq query that will return this structure of a product category with the array of products but only where a property of the product is equal to a value. For example where the price is 10.

I have tried

productCategory.SelectMany(p => p.Products).Where(x => x.Price == 10)

but this returns me an array of products but I want the product category with all the products that match the criteria.

As an extra challenge, I have multiple criteria as well, so in this example say prices are equal to 10, 30 or 50. There's an added complexity that one of the criteria has two values. I was going to do this just by calling the different selection criteria and then concatenate them at the end

var equalToTen = productCategory.Select... x.Price == 10
var equalToThirty = productCategory.Select... x.Price == 30
var equalToFiftyANdNameIsTest = productCategory.Select... x.Price == 50 && x.Name == "Test"

return equalToTen.Union(equalToThirty).Union(equalToFiftyANdNameIsTest)

but I'm sure that it can be done in a single statement without the need to concatenate at the end.

Update

The ProductCaegory isn't a single one either. It is the result of a EF query to the database. So it'll need to return multiple ProductCategories where the sub class Product query matches.

2
  • The problem here is that you want ProductCategorys as a result of your query but with a subset of elements in their Products property. This means that you either have to manipulate existing ProductCategory objects or create new ones. Commented Apr 29, 2021 at 15:35
  • @Dominik yes indeed, and as a LINQ query is not supposed to have side effects, we should not manipulate the originals Commented Apr 29, 2021 at 17:50

1 Answer 1

2

So you want the product categories with prices that are in some list, and only those prices?

var prices = new[]{10,30};
categories.Select(c => new ProductCategory {
  ProductCategoryId = c.ProductCategoryId,
  ProductCategoryName = c.ProductCategoryName,
  Products = c.Products.Where(p => prices.Any(pp => pp == p.Price) || (p.Price == 50 && p.Name == "Test")).ToList()
}).Where(p => p.Products.Count > 0);

This projects the categories to new cats with filtered products. Only products with the relevant price (10 or 30) (or 50 and called test) make the cut.. and then only categories that still have products are returned

The criteria line could be expressed as

Products = c.Products.Where(p => p.Price == 10 || p.Price == 30 || (p.Price == 50 && p.Name == "Test")).ToList()

Which might be easier to read; I originally had 3 items in the array, but I'll leave the array form in as a demo of a typical WHERE price IN (10,30,50) in LINQ - we sort of approach it from the other angle and ask "for these prices 10, 30, 50 is any of them equal to the p.Price" rather than "is p.Price in [10,30,50]". Other forms of simplification are possible such as .Where(p => prices.Contains(p.Price) ... but I tend to fall back to the Any route because it allows more complex constructs

Sign up to request clarification or add additional context in comments.

2 Comments

this looks good. I have my my problem though is that the original ProductCategory and Products are mapped entities to table and so I get an error that: "The entity or complex type 'ProductOption' cannot be constructed in a LINQ to Entities query."
If What is a ProductOption? In any case, just remove the ProductCategory (or Option: did you edit your class names for the purposes of the question?) from after the new to project to an anonymous type instead. It'll still work the same for most purposes and if you really need it as a ProductCategory you can map it back later

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.