webentwicklung-frage-antwort-db.com.de

Linq: GroupBy, Sum and Count

Ich habe eine Sammlung von Produkten

public class Product {

   public Product() { }

   public string ProductCode {get; set;}
   public decimal Price {get; set; }
   public string Name {get; set;}
}

Jetzt möchte ich die Sammlung basierend auf dem Produktcode gruppieren und ein Objekt zurückgeben, das den Namen, die Nummer oder die Produkte für jeden Code und den Gesamtpreis für jedes Produkt enthält.

public class ResultLine{

   public ResultLine() { }

   public string ProductName {get; set;}
   public string Price {get; set; }
   public string Quantity {get; set;}
}

Also benutze ich GroupBy, um nach ProductCode zu gruppieren, berechne dann die Summe und zähle auch die Anzahl der Datensätze für jeden Product Code.

Das habe ich bisher:

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

Aus irgendeinem Grund wird die Summe korrekt berechnet, aber die Zählung ist immer 1.

Sampe-Daten:

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

Ergebnis mit Beispieldaten:

Product1: count 1   - Price:13 (2x6.5)
Product2: count 1   - Price:12 (1x12)

Produkt 1 sollte count = 2 haben!

Ich habe versucht, dies in einer einfachen Konsolenanwendung zu simulieren, aber dort habe ich das folgende Ergebnis erhalten:

Product1: count 2   - Price:13 (2x6.5)
Product1: count 2   - Price:13 (2x6.5)
Product2: count 1   - Price:12 (1x12)

Produkt1: sollte nur einmal aufgeführt werden ... Der Code für das obige befindet sich auf Pastebin: http://Pastebin.com/cNHTBSie

111
ThdK

Ich verstehe nicht, woher das erste "Ergebnis mit Beispieldaten" kommt, aber das Problem in der Konsolen-App besteht darin, dass Sie SelectMany verwenden, um jeweils zu betrachten Element in jeder Gruppe .

Ich denke du willst nur:

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

Die Verwendung von First() zum Abrufen des Produktnamens setzt voraus, dass jedes Produkt mit demselben Produktcode denselben Produktnamen hat. Wie in den Kommentaren erwähnt, können Sie nach Produktname und Produktcode gruppieren. Dies führt zu denselben Ergebnissen, wenn der Name für einen bestimmten Code immer gleich ist, in EF jedoch anscheinend eine bessere SQL generiert.

Ich würde auch vorschlagen, dass Sie die Eigenschaften Quantity und Price in int und decimal ändern - warum sollten Sie eine Zeichenfolgeeigenschaft für Daten verwenden, die ist das eindeutig nicht textuell?

242
Jon Skeet

Die folgende Abfrage funktioniert. Dabei wird jede Gruppe anstelle von SelectMany für die Auswahl verwendet. SelectMany bearbeitet jedes Element aus jeder Sammlung. Beispielsweise haben Sie in Ihrer Abfrage ein Ergebnis von 2 Sammlungen. SelectMany erhält alle Ergebnisse, insgesamt 3, anstelle jeder Sammlung. Der folgende Code funktioniert für jedes IGrouping im ausgewählten Abschnitt, damit Ihre Aggregatvorgänge ordnungsgemäß funktionieren.

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

manchmal müssen Sie einige Felder mit FirstOrDefault() oder singleOrDefault() auswählen. Sie können die folgende Abfrage verwenden:

List<ResultLine> result = Lines
    .GroupBy(l => l.ProductCode)
    .Select(cl => new Models.ResultLine
            {
                ProductName = cl.select(x=>x.Name).FirstOrDefault(),
                Quantity = cl.Count().ToString(),
                Price = cl.Sum(c => c.Price).ToString(),
            }).ToList();
1
Mahdi Jalali