Reputation: 2580
Is there a more efficent way to write this LINQ statement?
Feature uses the same model as Product (as they are almost identical).
var products = db.Products.Where(q => q.IsFeature == false).ToList();
foreach (Product pr in products)
{
var features = db.Products.Where(q => q.ParentID == pr.ID).ToList();
foreach(Product feature in features)
{
pr.Features.Add(feature);
}
}
And the model:
public class Product
{
//START
public Product()
{
Features = new HashSet<Product>();
}
public int ID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public bool IsFeature { get; set; }
public int ParentID { get; set; }// If IsFeature is true we need to know its parent
//Nav Props
public ICollection<Product> Features { get; set; }// Features can be represented with the same model as product as they are almost identical
}
This does what I need ie a List of Product (Products), each with its own List of Feature (Features) but are there more elegant/efficient alternatives?
Upvotes: 2
Views: 505
Reputation: 4902
You need to use GroupJoin
for this. This is exactly what it's designed for.
var products = db.Products.Where(p => !p.IsFeature)
.GroupJoin(db.Products, p => p.ID, q => q.ParentID, (p, eq) => {
p.Features = eq.ToList();
return p;
})
.ToList();
This code is untested, but should work.
Upvotes: 0
Reputation: 13676
You're almost there you can use Addrange method of List collection:
var products = db.Products.Where(q => !q.IsFeature).ToList();
foreach(Product pr in db.Products)
if(!pr.IsFeature) pr.Features.AddRange(db.Products.Where(q => q.ParentID == pr.ID));
P.S. other idea is to extract features sort them, convert to array and use for loop :
var features = db.Products.Where(q => q.IsFeature).OrderBy(f => f.ParentID).ToArray();
p = db.Products.First(p => p.ID == features[0].ParentID);
for(int i = 0; i < features.Length; i++ )
{
if(i > 0 && features[i - 1].ParentID != features[i].ParentID)
p = db.Products.First(p => p.ID == features[i].ParentID);
p.Features.Add(features[i]);
}
Upvotes: 0
Reputation: 31686
If this operation has a complexity which rises to the point where the generated Linq's SQL becomes a hindrance or this business logic is done in multiple places; consider placing the logic in a stored procedure.
Once in the database simply map the results in EF which later can easily be consumed by the code. Hence the complexity is moved off of the client and directly on the database.
Steps (Database First)
Model Browser
.I discuss the ins-and-outs of mapping stored procedures on my blog article Entity Framework Stored Procedure Instructions.
Upvotes: 2
Reputation: 22001
What you need is a join
:
var productFeatures =
db.Products.Where(q => !q.IsFeature)
.Join(db.Products, p => p.ID, f => f.ParentId, (p, f) => new { product: p, features: f.ToList() })
.ToList();
For each object in db.Products.Where(q => !q.IsFeature)
this will find each of the db.Products
that have a ParentId
equal to its ID
field.
The results will be projected into a List()
of anonymous types with the fields product (Product)
and features (List<Product>)
Upvotes: 4