Laden...

Programm hängt und kommt auch an keinem Haltepunkt mehr an

Erstellt von Taucher vor 3 Jahren Letzter Beitrag vor 3 Jahren 1.138 Views
T
Taucher Themenstarter:in
307 Beiträge seit 2008
vor 3 Jahren
Programm hängt und kommt auch an keinem Haltepunkt mehr an

Hallo Leute,
ich habe ein Problem mit meinem Programm wo ich etwas Ratlos bin.

Also, ich habe eine Datenbank die ich mit Stadtnamen füllen will. Dies mache ich mittels einem Dateiimport.
Aber es scheint so als hängt sich das Programm nach kurzem auf und ich weiß leider nicht wieso. Haltepunkte werden dann auch nicht mehr erreicht, egal wo ich sie setze.
Kann sich jemand mal den Code ansehen und findet vielleicht den Fehler, oder kann einen Verdacht äußern. Danke schon einmal.
Ich stelle den Code für die Methoden so rein wie sie der Reihe nach Ablaufen.


try
            {
                string line;
                string[] splitted;
                List<string> imported = new List<string>();
                List<object> temp;
                Thread th;
                using (StreamReader sr = new StreamReader(this.txtFilePath.Text, Encoding.Default))
                {
                    int i = 0;
                    while(sr.Peek() >= 0)
                    {
                        temp = new List<object>();
                        line = sr.ReadLine();
                        splitted = line.Split(',');
                        if(splitted != null && splitted.Length == 2)
                        {
                            switch(kindOfImport)
                            {
                                case 0:
                                    //  1 = Stadt
                                    if (imported.Where(s => s == splitted[1]).FirstOrDefault() == null)
                                    {
                                        temp.Add(Import.kindOfImport.city);
                                        temp.Add(splitted);
                                        th = new Thread(this.databaseimport);
                                        th.Start(temp);
                                        imported.Add(splitted[1]);
                                    }
                                    i++;
                                    break;
                                case 1:
                                    //  1 = Bundesland
                                    if (imported.Where(s => s == splitted[1]).FirstOrDefault() == null)
                                    {
                                        temp.Add(Import.kindOfImport.state);
                                        temp.Add(splitted);
                                        th = new Thread(this.databaseimport);
                                        th.Start(temp);
                                        imported.Add(splitted[1]);
                                    }
                                    i++;
                                    break;
                                case 2:
                                    //  0 = plz
                                    if (imported.Where(s => s == splitted[1]).FirstOrDefault() == null)
                                    {
                                        temp.Add(Import.kindOfImport.city);
                                        temp.Add(splitted);
                                        th = new Thread(this.databaseimport);
                                        th.Start(temp);
                                        imported.Add(splitted[0]);
                                    }
                                    i++;
                                    break;
                            }
                        }
                        this.lblImportStatus.Content = i.ToString();
                    }
                }
                MessageBox.Show("Import erfolgreich abgeschlossen.");
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
            }


private void databaseimport(Object obj)
        {
            List<object> l = (List<object>)obj;
            Import.kindOfImport kind = (kindOfImport)l[0];
            string[] splitted = (string[])l[1];
            switch(kind)
            {
                case Import.kindOfImport.city:
                    CityManager.Current.Add(splitted[1], true);
                    break;
                case Import.kindOfImport.state:
                    StateManager.Current.Add(splitted[1], true);
                    break;
                case Import.kindOfImport.zipCode:
                    break;
            }
        }


public void Add(string cityName, bool ignoreExisting)
        {
            CityProvider.Current.Add(cityName, ignoreExisting);
        }


public void Add(string cityName, bool ignoreExisting)
        {
            this.Add(new List<string>() { cityName }, ignoreExisting);
        }


public void Add(IList<string> cityNames, bool ignoreExisting)
        {
            if (cityNames == null) throw new ArgumentNullException("Invalid city names.");
            if (cityNames.Count() == 0) return;

            using (ZipCodeEntities context = new ZipCodeEntities())
            {
                foreach(string city in cityNames)
                {
                    var temp = context.TB_City.Where(s => s.Name.ToLower() == city.ToLower()).FirstOrDefault();
                    if (temp != null && !ignoreExisting) throw new ArgumentException("Invalid city name. City name already exists.");
                    else if (temp != null && ignoreExisting) continue;

                    context.TB_City.Add(new TB_City() { Name = city });

                    using (var scope = TransactionScopeManager.Current.BuildTransactionScope())
                    {
                        context.SaveChanges();
                        scope.Complete();
                    }
                }
            }
        }

4.939 Beiträge seit 2008
vor 3 Jahren

Du kannst im Visual Studio einfach das Programm anhalten ("Break All") und dir im Aufrufliste-Fenster ("Call Stack") die Methode anschauen, in der das Programm hängt.

PS: Was soll denn der Thread-Code bewirken? Damit bringst du dir nur "race conditions " ein...
Und im case 2 greifst du, trotz Kommentar "0 = plz", einmal auf splitted[1] zu.

T
Taucher Themenstarter:in
307 Beiträge seit 2008
vor 3 Jahren

Ok, danke. Da werde ich dann mal nachsehen.

Ich habe die Aufrufe in Threads gelegt, weil ansonsten das Problem auftaucht das die Oberfläche nicht aktualisiert wird, das Fenster quasi hängt. Und da kam dann auch eine Fehlermeldung (ich weiß nicht mehr wie die war), und nach ner Runde googeln kam ich dann auf einen Beitrag von diesem Forum, das man Threads dafür verwenden soll. Deshalb. Ist das falsch?

Und das mit dem case 2 ist ein Schreibfehler von mir, stimmt. Is grad aber nicht so schlimm da die Methode dazu in der Methode "databaseimport" noch nicht aufgerufen wird.

16.835 Beiträge seit 2008
vor 3 Jahren

Ich habe die Aufrufe in Threads gelegt, weil ansonsten das Problem auftaucht das die Oberfläche nicht aktualisiert wird, das Fenster quasi hängt. Und da kam dann auch eine Fehlermeldung (ich weiß nicht mehr wie die war), und nach ner Runde googeln kam ich dann auf einen Beitrag von diesem Forum, das man Threads dafür verwenden soll.

Du meinst [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke) ?

Wenn Du den Beitrag ganz gelesen hättest, dann wüsstest Du, dass man dafür (mittlerweile) auch viel einfacher Tasks statt Threads verwenden kann.

Problem bei Deim Code ist halt auch, dass Du quasi alles irgendwie miteinander verwurstelt hast, statt den Code sauber zu trennen und sauber asynchron umzusetzen.
[Artikel] Drei-Schichten-Architektur
Und wegen der sehr sauberen Umsetzung hast Du Dir selbst Race Condition-Fallen gebaut.

Edit: mir ist aufgefallen, dass Du WPF verwendest.
Hier solltest Du ohnehin mit Datenbindung arbeiten und nicht mit den Control gepuzzle.

Dann hast Du die Problematik der Control-Aktualisierung aus einem Thread auch nicht.
Im Endeffekt also ein Folgefehler, weil Du mit WPF völlig falsch umgehst.

4.939 Beiträge seit 2008
vor 3 Jahren

Bzgl. der Threads: da hast du wohl etwas falsch verstanden. Es ist genau andersherum: wenn du Threads verwendest, dann kannst du nicht einfach so auf Controls (aus dem Hauptthread) zugreifen, s.a. [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke).

Außerdem solltest du bei WPF MVVM verwenden: [Artikel] MVVM und DataBinding sowie den Dateiimport in die Datenzugriffsschicht verlegen, s. [Artikel] Drei-Schichten-Architektur.

T
Taucher Themenstarter:in
307 Beiträge seit 2008
vor 3 Jahren

@Abt:

Problem bei Deim Code ist halt auch, dass Du quasi alles irgendwie miteinander verwurstelt hast, statt den Code sauber zu trennen und sauber asynchron umzusetzen.
[Artikel] Drei-Schichten-Architektur
Und wegen der sehr sauberen Umsetzung hast Du Dir selbst Race Condition-Fallen gebaut.

Ich denke schon das ich eine 3-Schichten Architektur habe, denn die Datenbankabfragen, Einträge usw. liegen in einem separatem Projekt. Sind also völlig unabhängig vom Modul was darauf zugreifen will.

Edit: mir ist aufgefallen, dass Du WPF verwendest.

Ja, ich verwende hier WPF, weil ich das einfach nur zum testen für die Datenbankebene verwenden wollte anstatt mir eine Internetseite dafür zu schreiben (Mein Schwerpunkt ist die Webentwicklung). Weil das einfach schneller geht. Denn mit WPF habe ich nicht wirklich viel gemacht und wollte das einfach auch verbinden um da ein bisschen mehr reinzukommen in WPF (Hat wohl nicht geklappt 😉 ) Deshalb auch keine Datenbindung.
Deshalb auch kein Testprojekt a la Unittest.

Danke schon mal für eure Hilfe und eure Ratschläge.

T
2.224 Beiträge seit 2008
vor 3 Jahren

Wenn du ein sauberes Drei-Schichten Modell hättest, warum gibst du dann was in einer MessageBox aus?
Auch füllst du dort ein Label mit der Zählvariable bzw. deren Wert.

Sowas ist Teil der UI und gehört nicht in deine Verarbeitung, die hier deine Business Logik darstellt.
Wenn es Fehler gibt, dann müssen diese für die Anzeige an die UI durchgereicht werden.

Auch nutzt man in .NET schon seit .NET 2.0 keine Threads mehr direkt, wenn es nicht wirklich nötig ist.
Heute kann man solche asynchrone Abläufe mit async/await über Tasks realisiert.
Dadurch hättest du auch gleich den Vorteil, dass du deinen Code sauber umsetzen kannst und bekommst auch gleich Threading über .NET spendiert.
Auch wirfst du mit jeder Zeile deinen aktuellen Thread weg und machst einen neuen auf.
Sowas kann auch zu Seiteneffekten führen und durch unmengen an Zeilen auch sinnlos viele Threads aufmachen.
Dadurch überlastest du sowohl die CPU als auch durch die ganzen DB Operationen auch deine Datenbank.

Anstelle von einzelnen Imports, wäre es sinnvoller die Daten erst zusammeln und dann in einem Rutsch via Bulk Insert zu importieren.
Mit EF (Core) kannst du dies entweder über AddRange oder wenn du direkt den DB Provider hast, dann über die entsprechenden Bulk Klassen, die auch schneller sind.

Gerade deine Fehler könntest du über eine saubere Modularisierung im Drei-Schichten Modell problemlos testen und beheben können.
Über UnitTests und Debuggen kannst du dann die Fehler finden und eleminieren.
Ebenfalls kannst du durch Unit Tests auch gleich absichern, dass zukünftige Änderungen an deinem Code nichts kaputt machen und durch Positiv/Negativ Tests gleich alle Fehlerfälle abdecken.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

16.835 Beiträge seit 2008
vor 3 Jahren

Ich denke schon das ich eine 3-Schichten Architektur habe

Sieht nicht so aus, da eben DAL und UI Code vermischt ist.
Da spielt auch keine Rolle wie man Projekte organisiert, wenn man Technologien und Verantwortlichkeiten (ver)mischt.