LINQ: объединение объединения и группировки по - PullRequest
50 голосов
/ 07 февраля 2012

У меня есть запрос, который объединяет объединение и группу, но у меня есть проблема. Запрос выглядит так:

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

Как вы видите, он объединяет таблицу Products с таблицей BaseProducts и группирует по id таблицы Product. Но в полученном ProductPriceMinMax мне также нужно свойство таблицы BaseProducts: bp.Name, но оно не знает bp.

Есть идеи, что я делаю не так?

Спасибо!

Ответы [ 3 ]

87 голосов
/ 07 февраля 2012

После того, как вы сделали это

group p by p.SomeId into pg  

, у вас больше нет доступа к переменным диапазона, использованным в начальном from.То есть вы больше не можете говорить о p или bp, вы можете говорить только о pg.

Теперь pg является группой и поэтому содержит более одного продукта.Все продукты в данной pg группе имеют одинаковый SomeId (поскольку это то, что вы сгруппировали), но я не знаю, означает ли это, что все они имеют одинаковый BaseProductId.

.чтобы получить название базового продукта, вам нужно выбрать конкретный продукт в группе pg (как вы делаете с SomeId и CountryCode), и , затем присоединиться к 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
 };

Тем не менее, это выглядит довольно необычно, и я думаю, вам следует отступить назад и подумать, что вы на самом деле пытаетесь получить.

25 голосов
/ 07 февраля 2012

Мы сделали это так:

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

РЕДАКТИРОВАТЬ: мы использовали версию AakashM, потому что она имеет лучшую производительность

10 голосов
/ 05 сентября 2016

Я столкнулся с той же проблемой, что и вы.

Я помещаю два tables result в t1 объект и группу 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
};
...