Средства 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 87

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(),
              };