web-dev-qa-db-fra.com

LINQ: combinant rejoindre et grouper par

J'ai une requête qui combine une jointure et un groupe, mais j'ai un problème. La requête est comme:

 var result = from p in Products                         
 join bp in BaseProducts on p.BaseProductId equals bp.Id                    
 group p by p.SomeId into pg                         
 select new ProductPriceMinMax { 
       SomeId = pg.FirstOrDefault().SomeId, 
       CountryCode = pg.FirstOrDefault().CountryCode, 
       MinPrice = pg.Min(m => m.Price), 
       MaxPrice = pg.Max(m => m.Price),
       BaseProductName = bp.Name  <------ can't use bp. 
 };

Comme vous le voyez, il joint la table Products à la table BaseProducts et se groupe sur un identifiant de la table Product. Mais dans le produit ProductPriceMinMax résultant, j'ai également besoin d'une propriété de la table BaseProducts: bp.Name, mais elle ne connaît pas bp.

Une idée de ce que je fais mal?

Merci!

50
L-Four

Une fois que vous avez fait cela

group p by p.SomeId into pg  

vous n'avez plus accès aux variables de plage utilisées dans le from initial. C'est-à-dire que vous ne pouvez plus parler de p ou de bp, vous ne pouvez parler que de pg.

Maintenant, pg est un groupe et contient donc plus d'un produit. Tous les produits d'un groupe pg donné ont le même SomeId (puisque c'est ce que vous avez groupé par), mais je ne sais pas si cela signifie qu'ils ont tous le même BaseProductId.

Pour obtenir un nom de produit de base, vous devez choisir un produit particulier dans le groupe pg (comme vous le faites avec SomeId et CountryCode), et puis rejoignez BaseProducts.

var result = from p in Products                         
 group p by p.SomeId into pg                         
 // join *after* group
 join bp in BaseProducts on pg.FirstOrDefault().BaseProductId equals bp.Id         
 select new ProductPriceMinMax { 
       SomeId = pg.FirstOrDefault().SomeId, 
       CountryCode = pg.FirstOrDefault().CountryCode, 
       MinPrice = pg.Min(m => m.Price), 
       MaxPrice = pg.Max(m => m.Price),
       BaseProductName = bp.Name  // now there is a 'bp' in scope
 };

Cela dit, cela semble assez inhabituel et je pense que vous devriez prendre du recul et considérer ce que vous essayez réellement de récupérer.

87
AakashM

Nous l'avons fait comme ça:

from p in Products                         
join bp in BaseProducts on p.BaseProductId equals bp.Id                    
where !string.IsNullOrEmpty(p.SomeId) && p.LastPublished >= lastDate                         
group new { p, bp } by new { p.SomeId } into pg    
let firstproductgroup = pg.FirstOrDefault()
let product = firstproductgroup.p
let baseproduct = firstproductgroup.bp
let minprice = pg.Min(m => m.p.Price)
let maxprice = pg.Max(m => m.p.Price)
select new ProductPriceMinMax
{
SomeId = product.SomeId,
BaseProductName = baseproduct.Name,
CountryCode = product.CountryCode,
MinPrice = minprice, 
MaxPrice = maxprice
};

EDIT: nous avons utilisé la version d’AakashM, car elle offre de meilleures performances.

25
L-Four

J'ai rencontré le même problème que toi.

J'enfonce deux tablesresult dans t1 objet et groupe t1.

 from p in Products                         
  join bp in BaseProducts on p.BaseProductId equals bp.Id
  select new {
   p,
   bp
  } into t1
 group t1 by t1.p.SomeId into g
 select new ProductPriceMinMax { 
  SomeId = g.FirstOrDefault().p.SomeId, 
  CountryCode = g.FirstOrDefault().p.CountryCode, 
  MinPrice = g.Min(m => m.bp.Price), 
  MaxPrice = g.Max(m => m.bp.Price),
  BaseProductName = g.FirstOrDefault().bp.Name
};
10
Milkker