C# Wertevergleich im Datatable

Nighthawk1977

Cadet 4th Year
Registriert
Sep. 2009
Beiträge
113
Hallo zusammen,

ich habe aktuell ein kleines Problem mit einem Programm, was ich in der Entwicklungsumgebung unserer ERP Software geschrieben habe.
Über eine SQL Abfrage hole ich mir Artikel-Nr., Benennung, Fertigungsauftragsmenge und kalk. Losgröße. Ziel ist, die kalk. Losgröße auf Basis der Medianmenge je Artikel neu zu setzen.

Zunächst filtere ich mir die individuellen Artikel-Nr., Benennungen und kalk. Losgrößen. Ich ergänze eine Spalte für den Median.

C#:
// Artikel filtern
public void artikel_filtern()
{
    try
    {
        DataView view = new DataView(faauswahl);
        artikel = view.ToTable(true, "artnr", "benennung", "klg");
        artikel.Columns.Add(new DataColumn("median_menge", typeof(int)));

        this.dgv_artikel.DataSource = artikel;
    }
    catch(Exception ex)
    {
        ShowError("Fehler: artikel_filtern", ex);
    }
}

median_menge habe ich als Int festgelegt da die kalk. Losgröße auch ein Int ist.

Im nächsten Schritt ermittele ich den Median, ggfs. steck hier in Zeile 31 und 32 schon das Problem. median habe ich zuvor als int definiert.

C#:
public void median_berechnen()
{
    try
    {
        foreach(DataRow row in artikel.Rows)
        {
            zaehlen = zaehlen + 1;
            this.progress1.Value = zaehlen;
            
            artnr = row["artnr"].ToString();
            
            DataView filter_artnr = new DataView(faauswahl);
            filter_artnr.RowFilter = String.Format(@"artnr = '{0}'", artnr);
            
            famengen = filter_artnr.ToTable();

            anzahl = famengen.Rows.Count;
            rest = famengen.Rows.Count % 2;
            
            if (rest == 0)
            {
                w1 = (anzahl / 2 -1);
                w2 = (anzahl / 2);
            }
            if (rest != 0)
            {
                w1 = (anzahl / 2);
                w2 = (anzahl / 2);
            }
            
            median = (famengen.Rows[w1]["famenge"].ToInteger() + famengen.Rows[w2]["famenge"].ToInteger()) / 2;
            row["median_menge"] = median;
        }
    }
    catch(Exception ex)
    {
        ShowError("Fehler: median_berechnen", ex);
    }
}

Da ich davon ausgehe, dass für viele Datensätze die kalk. Losgröße schon dem Median entspricht, möchte ich die Zeilen löschen.

C#:
public void daten_bereinigen()
{
    try
    {
        this.anzahl_gesamt = artikel.Rows.Count;
        
        for(int i = 0; i < artikel.Rows.Count; i++)
        {
            if (i>=0)
            {
                int a = artikel.Rows[i]["klg"].ToInteger();
                int b = artikel.Rows[i]["median_menge"].ToInteger();
                
                if (a == b)
                {
                    log.AppendText(string.Format(@"Artikel {0} entfernt." + "\n", artikel.Rows[i]["artnr"].ToString().Trim()));
                    artikel.Rows[i].Delete();
                }
                else
                if (a != b)
                {
                    log.AppendText(string.Format(@"Artikel {0} ändern." + "\n", artikel.Rows[i]["artnr"].ToString().Trim()));
                }
            }
        }
        
        // Löschungen im Datatable committen
        this.artikel.AcceptChanges();
    }
    catch(Exception ex)
    {
        ShowError("Fehler: daten_bereinigen", ex);
    }
}

Vermutlich habe ich hier Problem mit den Formaten. Von ursprünglich 264 Artikeln bleiben 132 übrig. Für diese 132 sind aber - zumindest in meiner Darstellung im Datagridview - die Werte identisch und müssten auch gelöscht werden. Ich habe aber keinen Eintrag in meiner TextBox, a != b kommt offenbar nicht vor.

Jemand eine Idee? Ich glaube, ich steh gerade nur auf dem Schlauch und es ist eigentlich ganz einfach...
 
Nur mal kurz drübergeguckt: Wenn du in Zeile 17 eine Row löscht, wird i trotzdem inkrementiert. D.h. du überspringst immer eine Row wenn du eine löschst. Du machst für jeden Artikel einen Logeintrag, das müsste da ja zu sehen sein, dass da Artikel nicht bearbeitet werden.
Wozu das i>=0 in Zeile 9? Kann i denn negativ werden?
 
Vielen Dank für den Hinweis, muss morgen mal gucken, ob mir damit vielleicht die gerade die Artikel übersprungen werden, die eigentlich rausfliegen müssten. Wäre hier eine for each Version sinniger?
Muss dazu sagen, dass mir das ganze Thema mehr oder weniger durch intensives learning by doing angeeignet habe und auch durch "Schulungen" durch die Consultants von unserem Anbieter. Da habe ich mir dann einiges an "Bausteinen" angesammelt die ich ich immer wieder verwende. Kann dann schon vielleicht schon mal eher rustikal als elegant sein.
Hier in dem Fall bräuchte ich das i>=0 in der Tat nicht, der Datatable artikel hat immer Einträge. Das ist so ein Klassiker eines Bausteins, ich hab auch schon mal Datatable die vielleicht keinen Eintrag haben.
 
Nighthawk1977 schrieb:
Hier in dem Fall bräuchte ich das i>=0 in der Tat nicht, der Datatable artikel hat immer Einträge. Das ist so ein Klassiker eines Bausteins, ich hab auch schon mal Datatable die vielleicht keinen Eintrag haben.
Da würde dir ein größer-gleich 0 aber auch nicht helfen.

Hat deine Entwicklungsumgebung keinen Debugger? Damit lassen sich solche Probleme innerhalb weniger Minuten finden.
 
Enurian schrieb:
Da würde dir ein größer-gleich 0 aber auch nicht helfen.

Mist, erwischt! ;)

Ja, einen Debugger gibt's grundsätzlich. Wobei ich da allerdings mit der Bedienung nicht so ganz vertraut bin. Versuche da noch mal mein Glück. Der Hinweis von Darlis, das ich Einträge überspringe, ist aus meiner Sicht eine heiße Spur.

Ich hab bislang vermutet, dass ich mit meinen Zahlenformaten ein Problem habe. Laut unserer Datenbankbeschreibung sind die Mengen aus dem Fertigungsauftrag Float, die ich in der Entwicklungsumgebung aber nur als Double ausgeben kann. Bei meiner Medianberechnung könnten halt x,5 rauskommen. Da ich für meine berechnete Losgröße aber ein Integer brauche - ist laut Datenbankbeschreibung Integer - hab ich versucht das passend zu "basteln".
 
Nighthawk1977 schrieb:
Wäre hier eine for each Version sinniger?
Die würde dir eine Exception an den Kopf schmeißen. Ändere nie eine Liste über die du iterierst! Man kann es mit for machen, hat dann aber mit Logikproblemen zu kämpfen, oder man nimmt einen anderen Ansatz.

Bei den Schulungen hat man dir vermutlich als best-practive beigebracht, ein try-catch in jede Methode zu setzen? Das Problem: Gibt es bei der erstem Methode einen Fehler, rechnen die anderen falsch weiter. Das ist i.d.R. nicht was man haben möchte. Mach das nur auf der obersten Ebene, bei Benutzereingaben, und evtl. dort wo es auch Sinn macht auf Fehler zu reagieren.
 
Ja, best-practice ist so eine Sache...
Gerade fällt mir auch ein, warum mir das löschen von Zeilen im Datatable in einer anderen Anwendung keine Probleme verursacht hat: Da hab ich nämlich pro Zeile in einer Spalte eine Summe berechnet. Im Anschluss dann alle mit null gelöscht. Rustikal...

Hmm, schlimm genug, dass ich mich um kurz nach 23 Uhr noch mit Arbeitsproblemen beschäftige. Aber der eine Kollege, der vom Fach ist, ist krank geschrieben und der eine Crack bei unserem Softwareanbieter ist ungefähr so gut erreichbar wie der Papst.

Ich schau mir morgen mal den Tipp bezüglich übersprungener Zeilen an. "Ändere nie eine Liste über die du iterierst!" werde ich mir merken, auch wenn ich "iterieren" ehrlich gesagt erstmal googlen musste... :heilig:
 
Ich habe gerade noch mal was ausprobiert, wenn ich mein Zeilenlöschen in Zeile 17 auskommentiere dann stehen alle 264 Artikel in meiner TextBox zum löschen.

Habe gerade noch mal Google bemüht und habe die Non-Linq Lösung aus diesem Beispiel Datatable - Delete a row in C-Sharp adaptiert zu

C#:
        List<DataRow> rowsToDelete = new List<DataRow>();
        foreach (DataRow row in artikel.Rows)
        {
            int a = row["klg"].ToInteger();
            int b = row["median_menge"].ToInteger();
          
            if (a == b)
            {
                log.AppendText(string.Format(@"Artikel {0} entfernt." + "\n", row["artnr"].ToString().Trim()));
                rowsToDelete.Add(row);
            }
        }
        foreach (DataRow row in rowsToDelete)
        {
            row.Delete();
        }
        artikel.AcceptChanges();

Damit habe ich dann für meine Artikelauswahl 0 Datensätze übrig. Das ist so auch korrekt weil ich dafür vorher schon mal die Schleife zum updaten der Losgrößen durchgeführt habe. Alles natürlich in unserer Testumgebung. :heilig: Für eine andere Artikelauswahl sieht es auch gut aus.

Ich habe nur noch nicht ganz durchschaut, warum das Programm in der zweiten Schleife weiß, dass die zu löschenden Zeilen in meinem Datatable artikel sind. Hätte jetzt erwartet, dass ich mich da noch mal auf den beziehen muss. Na, wie angedeutet, bin ich ursprünglich fachfremd...
 
Nighthawk1977 schrieb:
Ich habe nur noch nicht ganz durchschaut, warum das Programm in der zweiten Schleife weiß, dass die zu löschenden Zeilen in meinem Datatable artikel sind.
Ein DataTable weiß, welche Zeilen es enthält. Ob du auf die Zeile direkt über DataTable.Rows zugreifst oder erst in eine andere Liste packst oder als Parameter in einer Funktion übergibst spielt keine Rolle. Es werden immer nur Referenzen kopiert, die auf das selbe Objekt zeigen.
 
  • Gefällt mir
Reaktionen: Nighthawk1977
Zurück
Oben