Средства LINQ: метод groupBy, сумма и количество
у меня есть коллекция продуктов
public class Product {
public Product() { }
public string ProductCode {get; set;}
public decimal Price {get; set; }
public string Name {get; set;}
}
теперь я хочу сгруппировать коллекцию на основе кода продукта и вернуть объект, содержащий имя, количество или продукты для каждого кода и общую цену для каждого продукта.
public class ResultLine{
public ResultLine() { }
public string ProductName {get; set;}
public string Price {get; set; }
public string Quantity {get; set;}
}
поэтому я использую GroupBy для группировки по коду продукта, затем я вычисляю сумму, а также подсчитываю количество записей для каждого кода продукта.
это то, что у меня есть до сих пор:
List<Product> Lines = LoadProducts();
List<ResultLine> result = Lines
.GroupBy(l => l.ProductCode)
.SelectMany(cl => cl.Select(
csLine => new ResultLine
{
ProductName =csLine.Name,
Quantity = cl.Count().ToString(),
Price = cl.Sum(c => c.Price).ToString(),
})).ToList<ResultLine>();
для некоторых причина, сумма делается правильно, но счет всегда равен 1.
данные Sampe:
List<CartLine> Lines = new List<CartLine>();
Lines.Add(new CartLine() { ProductCode = "p1", Price = 6.5M, Name = "Product1" });
Lines.Add(new CartLine() { ProductCode = "p1", Price = 6.5M, Name = "Product1" });
Lines.Add(new CartLine() { ProductCode = "p2", Price = 12M, Name = "Product2" });
результат с образцом данных:
Product1: count 1 - Price:13 (2x6.5)
Product2: count 1 - Price:12 (1x12)
продукт 1 должен иметь счет = 2!
Я попытался смоделировать это в простом консольном приложении, но там я получил следующий результат:
Product1: count 2 - Price:13 (2x6.5)
Product1: count 2 - Price:13 (2x6.5)
Product2: count 1 - Price:12 (1x12)
Product1: должен быть указан только один раз... Код для вышеуказанного можно найти на pastebin:http://pastebin.com/cNHTBSie
2 ответа:
Я не понимаю, откуда берется первый "результат с образцами данных", но проблема в консольном приложении заключается в том, что вы используете
SelectManyпосмотреть каждый элемент в каждой группе.Я думаю, что вы просто хотите:
List<ResultLine> result = Lines .GroupBy(l => l.ProductCode) .Select(cl => new ResultLine { ProductName = cl.First().Name, Quantity = cl.Count().ToString(), Price = cl.Sum(c => c.Price).ToString(), }).ToList();использование
First()здесь для получения названия продукта предполагается, что каждый продукт с тем же кодом продукта имеет то же имя продукта. Как отмечено в комментариях, вы можете сгруппировать по названию продукта, а также код продукта, который даст те же результаты, если имя всегда одинаковое для любого заданного кода, но, по-видимому, генерирует лучший SQL в EF.Я также предлагаю вам изменить
QuantityиPriceсвойстваintиdecimalтипы соответственно - зачем использовать строковое свойство для данных, которые явно не текстовое?
следующий запрос работает. Он использует каждую группу, чтобы сделать выбор вместо
SelectMany.SelectManyработает над каждым элементом из каждой коллекции. Например, в запросе у вас есть результат 2 коллекции.SelectManyполучает все результаты, в общей сложности 3, вместо каждой коллекции. Следующий код работает на каждомIGroupingв выбранной части, чтобы ваши агрегатные операции работали правильно.var results = from line in Lines group line by line.ProductCode into g select new ResultLine { ProductName = g.First().Name, Price = g.Sum(_ => _.Price).ToString(), Quantity = g.Count().ToString(), };