Entwickler-Ecke

Datenbanken (inkl. ADO.NET) - Linq; Group by; Summierung von String-Werten


Oppi35 - Di 28.06.11 22:46
Titel: Linq; Group by; Summierung von String-Werten
Hallo Zusammen,

zu folg. Code ist folg. zu erwähnen:

Die Spalte "Monatsbeitrag" beinhaltet string-Werte. Dezimaltrenner ist das Komma (sofern man bei strings davon sprechen kann:))
Hier handelt es sich nur um eine Testanwendung. Dieses Problem habe ich eigentlich bei einer größeren DataTable, bei der ich die Daten aus einer CSV-Datei einlese.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
DataTable dt = new DataTable();
            dt.Columns.Add("Name");
            dt.Columns.Add("Monatsbeitrag");

            List<DataRow> rows = new List<DataRow>();
            for (int i = 0; i < 5; i++)
            {
                DataRow row = dt.NewRow();
                rows.Add(row);
            }

            rows[0]["Name"] = "Meier";
            rows[0]["Monatsbeitrag"] = "23,47";

            rows[1]["Name"] = "Schmidt";
            rows[1]["Monatsbeitrag"] = "33,47";

            rows[2]["Name"] = "Meier";
            rows[2]["Monatsbeitrag"] = "43,47";

            rows[3]["Name"] = "Meier";
            rows[3]["Monatsbeitrag"] = "53,47";

            foreach (DataRow row in rows)
            {
                dt.Rows.Add(row);                
            }

var query = from c in dt.AsEnumerable()
                        group c by new
                        {
                            c1 = c["Name"]
                        } into grp
                        select new
                        {
                            NameNeu=grp.Key.c1,
                            Jahresbeitrag = grp.Sum (r=>Convert.ToDecimal( r["Monatsbeitrag"]))
                        };

foreach (var item in query)
            {
                Console.WriteLine(item.Jahresbeitrag.ToString());
            }

            Console.Read();


Mein Problem ist jetzt die Invalid Cast Exception: "Ein Objekt kann nicht von DBNull in andere Typen umgewandelt werden."

Die Ursache ist, dass in der Schleife 3 neue Rows angelegt werden, die vierte aber Null ist. Wie kann die Summierung auch mit Null-Werten sauber durchgeführt werden?

Gruß
Frank


norman2306 - Mi 29.06.11 08:25

Um deine Frage exakt zu beantworten. Ersetze mal die Zeile


C#-Quelltext
1:
Jahresbeitrag = grp.Sum (r=>Convert.ToDecimal( r["Monatsbeitrag"]))                    


durch


C#-Quelltext
1:
Jahresbeitrag = grp.Sum (r => Convert.ToDecimal(r["Monatsbeitrag"] == DBNull.Value ? 0 : r["Monatsbeitrag"]))                    


Alternativ kannst du in deinem 'query' eine 'where'-Klausel einfügen:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
            var query = from c in dt.AsEnumerable()
                        where !(c.IsNull("Name") || c.IsNull("Monatsbeitrag"))
                        group c by new
                        {
                            c1 = c["Name"]
                        } into grp
                        select new
                        {
                            NameNeu = grp.Key.c1,
                            Jahresbeitrag = grp.Sum(r => Convert.ToDecimal(r["Monatsbeitrag"]))
                        };


Dadurch werden Null-Einträge ganz ausgeschlossen, was wohl dem eigentlichen Zweck besser dient.

Noch besser wird es, wenn du deinen Convert-Ausdruck durch ein Parse ersetzt:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
            decimal dummy;
            var query = from c in dt.AsEnumerable()
                        where !(c.IsNull("Name") || c.IsNull("Monatsbeitrag") || decimal.TryParse((string)c["Monatsbeitrag"], out dummy))
                        group c by new
                        {
                            c1 = c["Name"]
                        } into grp
                        select new
                        {
                            NameNeu = grp.Key.c1,
                            Jahresbeitrag = grp.Sum(r => decimal.Parse((string)r["Monatsbeitrag"]))
                        };


Oppi35 - Mi 29.06.11 16:38

Hallo Norman,

vielen Dank für Deine Hilfe.

Die folg. Zeile funktioniert nach einem ersten Test super.

C#-Quelltext
1:
Jahresbeitrag = grp.Sum (r => Convert.ToDecimal(r["Monatsbeitrag"] == DBNull.Value ? 0 : r["Monatsbeitrag"]))                    

Die Variante mit der Where-Klausel ist allerdings für mich nicht optimal.

Grund:
Wenn ich eine zweite Spalte habe, die ich zusätzlich summieren möchte, erhalte ich falsche Werte. Denn es werden ja mit der Where-Klausel schonmal alle Datensätze rausgefiltert, bei denen das Feld "Moantsbeitrag" == null ist; obwohl ein weiteres zu summierendes Feld event. Werte enthält.

Auf mein gegebenes Beispiel ist es aber natürlich so auch richtig.

Kannst Du mir den Teil hinter dem Gleichheitszeichen noch erläutern? "DBNull.Value ? 0 : r["Monatsbeitrag"]))"

Insbesondere die Syntax mit "? 0 :r" kenne ich leider nicht und konnte ich auch in keinem meiner Bücher finden.

Gruß
Frank

Moderiert von user profile iconTh69: C#-Tags hinzugefügt


Th69 - Mi 29.06.11 16:55

Hallo Frank,

s. Operator ?: [http://msdn.microsoft.com/de-de/library/ty67wk28.aspx]