Exceptions, und was sie uns sagen wollen

    • VB.NET

    Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

      Exceptions, und was sie uns sagen wollen

      In AvoidTryCatch versuche ich im Grunde vor allem, der geneigten Leserschaft nahezubringen, dass Fehlermeldungen nicht unsere Feinde sind, sondern unsere besten Freunde. Die Feinde sind ja die gemeldeten Fehler, und v.a. derjenige der sie eingebaut hat, nämlich ... ähm ... naja egal ;).

      Jo, und wie das mit richtigen Freunden so ist, muß man sich mit ihnen auseinandersetzen, und versuchen zu verstehen, was sie sagen.
      Also bei den Fehlermeldungs-Freunden (ich gebs ja zu: es sind eher Petzen) muß man sie erstmal aufmerksam lesen. Gelegentlich stellt sich ein Verständnis dann von selbst ein.

      Ich fange mal mit den einfachsten an, und arbeite mich dann vor, ja?
      1. FileNotFoundException
        wirklich einfach, oder? Die Message dieser Exception gibt uns sogar den genauen Pfad an, der nicht gefunden wurde. Und den Pfad kann man sogar kopieren und im Forum posten, und das ist manchmal eine gute Idee, weil vlt. enthält er ja komische Buchstaben oder anneren Unsinn: "c:\Blabla\\GibtsNich.txt"

      2. DirectoryNotFoundException: der Zwillings-Bruder von 1.

      3. ArgumentOutOfRangeException:
        ist leicht zu demonstrieren, und (Kenntnis der .SubString()-Methode vorausgesetzt) ist auch gleich klar, wasse uns sagen will:

        VB.NET-Quellcode

        1. Dim s = "Hallo".SubString(99)
        Natürlich kann man keinen Substring bilden, der beim 99. Zeichen anfängt, wenn der String selbst nur 5 Zeichen lang ist - also 99 als Argument ist sowas von Out Of Range!

      4. IndexOutOfRangeException:
        Dieselbe Problematik wie bei ArgumentOutOfRange - nur isses hier kein Methoden-Argument, das out of range geht, sondern ein Index

        VB.NET-Quellcode

        1. Dim failChar = "Hallo"(99)


      5. NullReferenceException 1
        "Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt."
        zu deutsch: Irgendeine Variable oder ein Ausdruck ist Nothing, und trotzdem wird versucht, was damit zu tun.
        Simpel-Beispiel:

        VB.NET-Quellcode

        1. Dim s As String
        2. Dim length = s.Length
        Keine gute Idee, von einer String-Variable, die noch gar keinen String enthält, die .Length - Property abzurufen.
        Aber das kann auch schwieriger sein, nämlich, wenns ein Ausdruck ist, der eigentlich was sein sollte, aber nichts ist

        VB.NET-Quellcode

        1. Using sr = New StringReader("Hallo Welt")
        2. Dim length = 0
        3. Do
        4. length += sr.ReadLine().Length
        5. Loop Until length = 99
        6. End Using
        Hier wird length += sr.ReadLine.Length angemeckert, und zwar nicht, weil sr Nothing wäre, sondern der Ausdruck sr.ReadLine() ergibt Nothing, wenn der Reader am Ende ist. Und (s.o.) von Nothing kann man keine .Length abrufen.

      6. NullReferenceException 2: nicht initialisiertes Array
        Aus besonderem Anlass (2 Nachfragen innerhalb einer Stunde):

        VB.NET-Quellcode

        1. Dim P() As Byte
        2. For i = 0 To 127
        3. P(i) = CByte(i * 2)
        4. Next
        P ist eine Variable für ein Array, aber ist kein Array drinne, sondern Nothing. Daher weisser bei P(i) nix anneres zu tun, als eine NullReferenceException zu schmeißen.

        Da muß man jetzt Grundschule wiederholen: "Wie deklariere ich ein Array mit 128 Elementen drinne?" Richtige Antwort:

        VB.NET-Quellcode

        1. Dim P(127) As Byte


      7. InvalidCastException 1
        In Option Strict On! erläutere ich, wie unschlau es ist, den Compiler mittels Option Strict Off anzuweisen, nach eigenem Ermessen Typumwandlungen vorzunehmen.
        Wer sich das zu Herzen nimmt, fühlt sich vlt. zunächst genötigt, nun alle Typumwandlungen explizit hinzuschreiben, gewissermaßen aus dem einem Unfug einen anderen zu machen:

        VB.NET-Quellcode

        1. Me.Dock = "Fill"
        2. 'wird zu
        3. Me.Dock = CType("Fill", DockStyle)
        Das ist trotzdem ein Fortschritt, denn nun ist klar ersichtlich, welcher Unfug da steht, und die InvalidCastException, die's bemeckert ("Ungültige Konvertierung von der Zeichenfolge Fill in Typ Integer.") sagts uns: "Aus einem String kann man keinen Dockstyle machen."

        Die Lösung ist einfach wie nur was: Einfach es richtig machen, und der Dock-Property vom Typ DockStyle logisch auch einen Wert vom Typ DockStyle zuweisen:

        VB.NET-Quellcode

        1. Me.Dock = DockStyle.Fill
        Das ist das Grundprinzip streng typisierter Programmiersprachen.

      8. InvalidCastException 2: For Each ... As ...
        Es gibt auch weniger ersichtliche InvalidCasts (ungültige Typumwandlungen):

        VB.NET-Quellcode

        1. For Each bt As Button In Me.Controls
        2. bt.PerformClick()
        3. Next
        Gleich zeile#1: "Das Objekt des Typs "System.Windows.Forms.SplitContainer" kann nicht in Typ "System.Windows.Forms.Button" umgewandelt werden."
        Wo, bitte ist denn hier eine Typumwandlung? Jahaa! das As Button im Schleifenkopf weist an, alle Controls dieser Schleife als Button aufzufassen - in diesem Falle dumm gelaufen, denn da war wohl ein Splitcontainer mit drin, und das ist halt kein Button, und kann man auch keinen Button draus machen.

      9. InvalidCastException 3: TypUmwandlungen allgemein problematisch
        Das kritische an TypUmwandlungen ist ja: manchmal gehts und manchmal nicht:

        VB.NET-Quellcode

        1. Dim ctl As Control
        2. ctl = Button1
        3. Dim bt = DirectCast(ctl, Button) 'geht
        4. ctl = SplitContainer1
        5. bt = DirectCast(ctl, Button) 'geht nicht
        Eine TypUmwandlung (Cast) weist den Compiler an, das Gecastete als etwa anneres aufzufassen, und der sagt auch nix dazu, sondern denkt "Mein Programmierer wird schon wissen, wassertut". Dass sich Compilerchen da aber irrt, meldet uns InvalidCastException dann zur Laufzeit.

        Normalerweise - Option Strict On! vorrausgesetzt - sagt uns der Compiler nämlich schon Bescheid, bevor er einen zum Scheitern verurteilten Testlauf unterfängt - Beispielsweise

        VB.NET-Quellcode

        1. Dim bt As Button = Me.Controls(0)
        ""Option Strict On" lässt keine impliziten Konvertierungen von System.Windows.Forms.Control in System.Windows.Forms.Button zu.", lässt er uns wissen, und meint damit, dass nicht sicher ist, ob das erste der Controls auch wirklich ein Button ist, und daher solleman sowas besser lassen. (Falls wir es aber noch besser wissen - verwendemer halt einen Cast :))

      10. InvalidOperationException 1: "Die Auflistung wurde geändert..."
        "InvalidOperation", also dass eine Operation nicht funktioniert sagt uns im Grunde garnix.
        Aber manchmal (eher selten) ist immerhin der Meldungs-Text aufschlußreich. Beispiel:

        VB.NET-Quellcode

        1. ( Dim ints as List(Of Integer) = Enumerable.Range(5, 21).ToList() )
        2. For Each i In ints
        3. 'grade zahlen entfernen
        4. If i Mod 2 = 0 Then ints.Remove(i)
        5. Next
        "Die Auflistung wurde geändert. Der Enumerationsvorgang kann möglicherweise nicht ausgeführt werden."
        Nicht wahr: Wenn man drüber nachdenkt, ists eine bescheuerte Idee, aus einer Liste, die man grade durchläuft, gleichzeitig bestimmte Items rauszuschmeißen.
        For Each macht das einfach nicht mit, und das ist ja auch ganz gut so.

        Na, machnwas doch ohne das blöde ForEach, gibt ja noch For i:

        VB.NET-Quellcode

        1. For i = 0 To ints.Count - 1
        2. If ints(i) Mod 2 = 0 Then ints.RemoveAt(i)
        3. Next
        Hier wird dann eine OutOfRange-Exception (s.o.) gewissermaßen die Frage aufwerfen, wer denn hier der Blöde ist ;). Denn das entfernen eines Items verringert ja ints.Count, und deshalb geht der i - Zähler gegen Ende out of Range.

        Aber es geht durchaus - For i ist manchmal nämlich garnet blöde:

        VB.NET-Quellcode

        1. For i = ints.Count - 1 To 0 Step -1
        2. If ints(i) Mod 2 = 0 Then ints.RemoveAt(i)
        3. Next
        (Merke: Für eine Rauswerf-Suche immer rückwärts durch die Auflistung laufen)

      11. InvalidOperationException 2: "ungültiger threadübergreifender Vorgang"

        (eine vorbildlich unverständliche Fehlermeldung :thumbsup: )
        Scheint iwas mit Threading zu tun zu haben, aber manchmal ist man sich ühaupt keiner Schuld bewusst, etwa, wenn man ein FileSystemWatcher-Event verarbeitet - wie soll man denn drauf kommen, dass der sein Event im Nebenthread feuert? (na - einfach: durch diese Exception kommt man drauf ;))
        Und wieso soll das ungültig sein - der gezeigte Code ist doch der allerharmloseste?
        Is eben so: Aus einem NebenThread darf man ein Steuerelementen nicht verändern. Properties auslesen ist ok, aber Properties setzen wird mit dieser Exception quittiert.
        Die Lösung besteht im Invoking, und wenn man auf VBParadise die SuFu betätigt, wird man erschlagen von Lösungen, und die meisten sind suboptimal.
        Hier mal eine optimale Lösung:

        VB.NET-Quellcode

        1. Sub foo()
        2. Me.BeginInvoke(Sub() Label1.Text = "foo")
        3. End Sub
        Der Vorgang wird in eine anonyme Methode verlagert, und diese wird mittels Control.BeginInvoke() an den HauptThread delegiert.
        Optimal ist diese Lösung im Vergleich zu Control.Invoke(), denn bei letzterem wartet der NebenThread ganz unnötigerweise, bis der HauptThread den Vorgang erledigt hat. (Merkt man im HauptThread nix von, aber der Nebenthread ist ausgebremst, für den Moment.)
        Auch so üblich wie suboptimal ist ein vorheriger Test auf Control.InvokeRequired(). Denn ich weiß ja, dass der Aufruf aus einem Nebenthread erfolgt - da brauch ich nicht drauf zu testen.
        Es ist übrigens egal, welches Control mit dem Invoking beauftragt wird, denn alle Controls (ausser ggfs. SplashScreens) sind im selben Thread erstellt: im HauptThread. In obigem Beispiel wurde das Form selbst herangezogen (ist ja auch ein Control) - ebenso gültig wäre:

        VB.NET-Quellcode

        1. Label1.BeginInvoke(Sub() Label1.Text = "foo")
        Und eine besonders allgemeingültige Formulierung wäre:

        VB.NET-Quellcode

        1. Application.OpenForms(0).BeginInvoke(Sub() Label1.Text = "foo")
        Denn die OpenForms - Auflistung ist aus jeder Klasse und aus jedem Modul heraus abrufbar.

      "Brute-Force-Debugging" (am Beispiel NullReferenceException)
      In langen, zusammengesetzten Ausdrücken hat man u.U. enorm Mühe, zu erkennen, welcher Teil genau den Fehler verursacht (etwa Nothing ergibt) - denn der Debugger kann uns nur die ganze Zeile als Fehler zeigen. Beispiel aus der Praxis:

      VB.NET-Quellcode

      1. Selection = Globals.tblXY.Range(workSheet.Cells(1, 2), workSheet.Cells(Selection.Rows.Count, Selection.Columns.Count))
      Wenn man nun garkeine Idee hat, was von dem Bandwurm Nothing ist, dann kannman mit "Brute-Force" den Bösewicht enttarnen: nämlich man löst den langen Ausdruck in seine Teil-Ausdrücke auf, und jeder Teil belegt dann eine Zeile:

      VB.NET-Quellcode

      1. ' Selection = Globals.tblXY.Range(workSheet.Cells(1, 2), workSheet.Cells(Selection.Rows.Count, Selection.Columns.Count))
      2. Dim cells= worksheet.Cells
      3. dim cell=cells(1,2)
      4. dim rows=Selection.Rows
      5. dim columns=Selection.Columns
      6. dim rowCount=rows.Count
      7. dim colCount=columns.Count
      8. dim cell2=cells(rowCount, colCount)
      9. dim tblXy=Globals.tblXY
      10. Selection =tblXy.Range(cell, cell2) ' entspricht dem oben auskommentierten Gesamt-Ausdruck
      Jetzt wird die Exception die eine Zeile anzeigen, in der das böse Objekt eindeutig identifizierbar ist. ;)
      Wie gesagt - "Brute-Force" ("viehische Gewalt") - es gibt elegantere Möglichkeiten zu debuggen.

      Beachte: "Brute-Force-Debugging" (Auflösen eines komplexen Ausdrucks in mehrere "atomare" Einzelausdrücke) ist anwendbar für jede Art Exception, um den Fehler genauer zu lokalisieren.

      InnerExceptions
      Häufig beinhaltet eine Exception eine annere Exception, die sog. "InnerException". Dann nämlich, wenn die eigentliche Exception bereits intern gefangen wurde, und dann wurde eine weitere Exception geworfen, die die ursprüngliche Exception beinhaltet und mit weiteren Informationen anreichert (ReThrowing). Also immer auch gucken, ob eine InnerException gegeben ist, und was diese uns wohl sagen will.

      Besondere Tücke mit Fehlern beim Programm-Start-up
      Beim Startup versagt die IDE häufig, und bringt nur die Fehlermeldung, und keinen Code-Stop und den ganzen Debugging-Komfort. Erste Maßnahme ist, jeden User-Code aus der Sub New des MainForms und aus dem Form_Load herauszunehmen.
      Stattdessen kann man meist die grundlegenden Einstellungen ebensogut im Form_Shown - Event vornehmen, oder man macht gar testweise einen Extra-Button, der den eigentlichen Startup ausführt, nachdem das Form schonmal sichtbar ist.
      Kommts weiterhin schon während des Startups zu Fehlermeldungen, dann liegt der Fehler vmtl. in der InitializeComponents() - Methode des Designer-Codes des Forms.
      Sowas tritt eiglich nur auf, wenn man selbstgebastelte oder 3rd-part-Controls verwendet, und die buggy sind.

      Ja, soweit erstmal. Vielleicht fallen mir ja noch gelegentlich weitere Exception-Freunde auf, deren Aussagen der Kommunikations-Beratung bedürfen - dann werdich sie gelegentlich noch zufügen.
      Diesbezügliche Tipps per PM oder gleich als Antwort könnten da hilfreich sein.

      Dieser Beitrag wurde bereits 32 mal editiert, zuletzt von „ErfinderDesRades“ ()

      ErfinderDesRades schrieb:

      FileNotFoundException
      funktioniert leider nicht überall. :S

      VB.NET-Quellcode

      1. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
      2. Dim bmp As New Bitmap("not_available.bmp")
      3. End Sub
      Der GDI meldet sich hier leider etwas unlogisch mit einer ArgumentException:
      Bilder
      • Exception.jpg

        29,21 kB, 467×310, 490 mal angesehen
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      Jo, ich hab wirklich nur Simpel-Exceptions genommen, die eindeutig sind und einfach zu besprechen. ArgumentException etwa ist eine sehr unspezifische Exception, da kann man nur hoffen, aus dem Meldungstext und der Fehlerzeile iwie die nötigen Informationen zu ersehen.
      Auch unangenehm die InvalidOperationException, deren Bezeichnung ja noch weniger aussagekräftig ist, als das mit dem Argument.
      Aber grad bei InvalidOperation gibts meist eine InnerException, die weitere Infos liefert.
      Ich bin nur grad zu doof, um eine InvalidOperation zu provozieren, sonst könnte ich ein bildchen anhängen, was man sieht, wenn man auf "Details anzeigen" der Fehlermeldung klickst.
      Jetzt wollte ich mit

      VB.NET-Quellcode

      1. Dim d As Double = Math.Acos(2)
      so einen Fehler provozieren, aber es gelang mir nicht.
      Gibt es nicht eine Einstellung, dass Flotingpoint-Exceptione sicht ignoriert werden sollen? Hab sie bei mir nicht gefunden (VS2010-Ultimate).
      Bilder
      • acos.jpg

        7,51 kB, 348×67, 394 mal angesehen
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!

      CompilerFehler

      Im ersten Post liegt ja der Schwerpunkt schwer auf Exceptions, also Laufzeitfehler.

      Dabei gibts noch eine annere Art der Fehlermeldung, nämlich die Compiler-Fehler.
      Compiler-Fehler sind "grobe Fehler", also sind Fehler, die bereits der Compiler erkennt, bemeckert, und von vornherein den Testlauf vereitelt. Derlei Fehler sind viel einfacher zu debuggen, weil sie immer, und schon von vornherein erkannt werden.
      Laufzeitfehler hingegen können nur erkannt werden, wenn der Code tatsächlich durchlaufen wird, und dann auch nur unter unglücklichen Umständen (ich sage ja, es sind glückliche Umstände, denn anners blieben die Fehler ja unerkannt).
      1. VB.NET-Quellcode

        1. Dim i As Integer = "99"
        Keine Frage - Blödsinn: Ein String ist kein Integer. Der Compiler formuliert das Problem so:
        ""Option Strict On" lässt keine impliziten Konvertierungen von String in Integer zu."
        Damit spielt er auf die Möglichkeit an, Option Strict auszuschalten, aber wie schon in post#1 zur InvalidCastException ausgeführt, ist das das dümmste, was man machen kann.
        Prinzipiell soll man immer versuchen, ohne Konvertierungen zu proggen. Denn Konvertierungen sind unperformant und bergen immer die Gefahr von Laufzeitfehlern.
        Hier ist die Lösung ja auch zum piepen einfach:

        VB.NET-Quellcode

        1. Dim i As Integer = 99


      2. VB.NET-Quellcode

        1. Public Class Class1
        2. Public Bla As String = "Bla"
        3. Public Shared BlaBla As String = "BlaBla"
        4. End Class
        5. Public Class Tester
        6. Public Sub Class1Tester()
        7. Dim sBlaFail = Class1.Bla 'Der Verweis auf einen nicht
        8. '° freigegebenen Member erfordert einen Objektverweis.
        9. Dim c1 = New Class1
        10. Dim sBlaSuccess = c1.Bla
        11. Dim sBlaBlaSuccess = Class1.BlaBla
        12. End Sub
        13. End Class
        "Der Verweis auf einen nicht freigegebenen Member erfordert einen Objektverweis."
        Wir sehen: Fehlermeldungen nehmen keinerlei Rücksicht darauf, ob jmd. Anfänger ist. Die Beherrschung der Sprache wird einfach vorausgesetzt. Das geht auch nicht anders, denn ein Compiler soll korrekten Code kompilieren, und kann kein universales Lehrbuch für Einsteiger jeglichen Niveaus und für jegliches Teilgebiet sein.
        Diese Meldung etwa setzt vorraus, dass "freigegebener Member" verstanden wird als Public Shared (oder unter anneren Umständen auch Private Shared).
        Shared Variablen sind keine Objekt-Variablen, bei denen jedes Objekt eine solche hat, sondern es sind Unikate, die nur einmalig existieren, egal, wieviele Objekte der Klasse erstellt sind. (Braucht man selten, aber braucht man, etwa um Einstellungen zu implementieren, die bei allen Objekten der Klasse dieselben sein sollen). Auf Shared Variablen haben also alle Objekte Zugriff, daher Shared: sie teilen sich diese Variable.
        Es gibt auch Shared Methoden, Properties oder auch Events - deshalb spricht der CompilerFehler auch von "freigegebenen Membern" - man sieht wieder: Fehlermeldungen sind immer 100% korrekt, und grad deshalb oft nur schwer verständlich.
        Jedenfalls, obiger Code verwendet "Class1" wie ein Objekt, aber Class1 ist kein Objekt, sondern ist eine Klasse.
        Zeile 10 und 11 folgen dann dem Hinweis des Compilers: Nämlich es wird ein Objekt vom Typ Class1 erstellt, und dieses Objekt kann dann auch auf die Variable "Bla" zugreifen. (c1 ist der geforderte Objekt-Verweis)
        Zeile 13 demonstriert die Verwendung einer Shared Variable. Hier ists korrekt, "Class1" als Referenz anzugeben, denn BlaBla ist Shared, und kann über den KlassenNamen referenziert werden, ohne dass ein konkretes Objekt dieser Klasse angegeben werden muß.
        Grad VB-Progger tun sich mit der Unterscheidung von Klasse und konkretem Objekt einer Klasse schwer - ich räsonniere darüber in VeryBasics.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

      InvalidOperationException:

      Mir passiert da eine solche Ausnahme.
      Sie wird im Application.Designercode generiert.
      Der Ursprung der Auslösung liegt im Form1.Designercode ab Zeile 42 (Private Sub InitializeComponent() ... siehe Bilder)
      Wenn ich die Ereignisprozedur
      Private Sub DGV_CellValueChanged(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs) Handles DGV.CellValueChanged
      in der Form1 auskommentiere, ist der Fehler weg? 8|
      Warum, kann ich nicht sagen. Die InnerException's sind in diesem Zusammenhang mir nicht recht klar :?:

      Spoiler anzeigen

      VB.NET-Quellcode

      1. Option Strict On
      2. Public Class Form1
      3. Dim intList As New List(Of Integer) From {1, 2, 3, 4}
      4. Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
      5. cbxCol.ValueType = GetType(Int32)
      6. End Sub
      7. Private Sub DGV_CellBeginEdit(ByVal sender As Object, ByVal e As DataGridViewCellCancelEventArgs) Handles DGV.CellBeginEdit
      8. Dim cbxCell As DataGridViewComboBoxCell = TryCast(DGV.Rows(e.RowIndex).Cells(e.ColumnIndex), DataGridViewComboBoxCell)
      9. If e.ColumnIndex <> 0 OrElse Not e.RowIndex.IsBetween(0, DGV.RowCount - 1) Then Return
      10. cbxCell.Items.Clear()
      11. cbxCell.Items.AddRange(intList)
      12. End Sub
      13. Private Sub DGV_CellValueChanged(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs) Handles DGV.CellValueChanged
      14. If (e.ColumnIndex = 0) Then
      15. intList.Remove(CInt(DGV(e.ColumnIndex, e.RowIndex).Value))
      16. End If
      17. End Sub
      18. End Class
      Bilder
      • InvalidOperationException.PNG

        96,46 kB, 1.646×475, 443 mal angesehen
      • InvalidOperationException Auslöser.PNG

        74,91 kB, 958×698, 450 mal angesehen

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VB1963“ ()

      Fragen sinnvoll stellen

      (äh - dies ist keine Antwort auf den Vorposter, sondern ich möchte die 5 Regeln einfach als eigenen Post formuliert haben, dass man drauf verlinken kann)

      Hier nochmal die 5 Regeln für sinnvolle Fragen:
      1. Name der Exception und MeldungsText angeben
      2. Code-Ausschnitt posten
      3. Fehlerzeile angeben
      4. Fehlverhalten angeben
      5. Erwünschtes Verhalten angeben
      Nicht immer sind alle 5 Punkte ausdrücklich abzuarbeiten, etwa, wenn eine Exception auftritt, kann man sich - soweit nichts anneres gesagt wird - das erwünschte Verhalten natürlich denken (nämlich, dass sie nicht auftritt).
      Aber implizit sollte man die Punkte doch durchgehen, also den Post nochmal nachlesen, und prüfen, ob alle diese Fragen geklärt sind oder wirklich selbsterklärend (auch für dummies).

      Zusatz für Probleme mit eingebetteten Strings (Regel #6):
      ZB. Sql-Probleme kann man nur lösen, wenn man den fehlerhaften CommandText auch sieht. Etwa sowas ist prinzipiell nicht zu beurteilen:

      VB.NET-Quellcode

      1. Dim sql As String = "SELECT count(*) FROM OV WHERE (kundennummer='" & tb_kundennummer.Text & "')"
      2. Dim cmd As New OleDbCommand(sql, con)
      3. dim reader = cmd.ExecuteReader()
      Denn man weiß ja nicht, was in tb_kundennummer drinne steht 8| .
      Bei Problemen mit DBCommands muß also zusätzlich zum VB-Code auch der Sql-Code gezeigt werden. Dazu startet man einen Testlauf mit gesetztem Haltepunkt (hier auf zeile#3).
      Den Inhalt von cmd.CommandText füge man im Frage-Post ein - etwa:

      SQL-Abfrage

      1. SELECT count(*) FROM OV WHERE (kundennummer='0815')

      (Aber im Grunde soll man so'n Crap auch garnet coden, sondern immer DbParameter verwenden.)

      Das man die Strings sehen muß, um ihre Fehlerhaftigkeit beurteilen zu können, gilt natürlich nicht nur für Sql, sondern überall, wo String-Angaben ausgewertet werden: Ob nun ein WebRequest failt, ein Regex, oder eine Datei-Operation - was kannman schon zu sowas sagen?

      VB.NET-Quellcode

      1. My.Computer.FileSystem.CopyFile(Textbox1.Text, Textbox2.Text)

      Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „ErfinderDesRades“ ()

      Memo schrieb:

      damit man auf alles vorbereitet ist
      Da steht leider nicht alles drin. Bei

      VB.NET-Quellcode

      1. Dim bmp = New Bitmap("Pfad nicht vorhanden")
      steht
      Ausnahmen:
      System.IO.FileNotFoundException: Die angegebene Datei wurde nicht gefunden.
      und es kommt:
      Bilder
      • Pfad.jpg

        29,88 kB, 512×296, 423 mal angesehen
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!

      InvalidOperationException: Für ein Update ist ein gültiger UpdateCommand erforderlich

      InvalidOperationException: Für ein Update ist ein gültiger UpdateCommand erforderlich, wenn eine DataRow-Auflistung mit modifizierten Zeilen weitergegeben wird.
      Dassis glaub die Lieblings-Exception für Einsteiger in Datenbanking, die die GrundIdee relationaler Datenbanken ühaupt noch nicht verstanden haben.
      Meist haben sie bereits bei Anlage einer Tabelle in der Datenbank versäumt, eine Primärschlüssel-Spalte (ID) zu definieren.

      Nun kann aber ein geänderter Datensatz nicht geupdated werden, wenn keine ID spezifiziert ist, denn ohne ID weiß die Datenbank nicht, in welchen der Datensätze der Tabelle die Änderungen einzutragen sind.

      Der Ado.Net-Assistent kann folglich auch kein UpdateCommand anlegen, wenner den typisierten TableAdapter generiert, der fürs Laden und Speichern zuständig ist.

      Jo, und dann kommts zu dem Fehler, dass der TableAdapter nicht updaten kann, weiler kein UpdateCommand hat, weil der Datenbank-Assistent keines anlegen konnte, weil die Datenbank-Tabelle keinen ID-Spalte hat, weil der Progger datenbänkert ohne das nötige Grundlagenwissen.

      Die Lösung ist umständlich, (wie so oft, wenn ein früher Fehler spät erkannt wird):
      1. Projekt-Backup machen - vlt. auch gleich den SolutionExplorer downloaden und einrichten. Denn jederzeit lauffähige Backups machen zu können ist sowieso unerhört wichtig.
      2. Datenbank korrigieren: ID-Spalte einbauen
      3. das fehlerhafte typisierte Dataset löschen
        Hierbei entstehen zwangsläufig viele Compiler-Fehler, denn im Projekt wird vielfach aufs Dataset zugegriffen, und das ist jetzt erstmal weg.
      4. Dataset neu generieren, unter exakt demselben Namen wie vorher.
      5. Je nachdem, wie umfangreich die Änderungen waren, verbliebene Fehler nachbessern.
        Besonders Fehler im Codebehind der Forms sind unangenehm, denn dann kann der Designer das Form nicht mehr anzeigen, und man kann also im Designer auch nix mehr korrigieren ;(
        Das führt oft dazu, dass ein ganzes Form irreparabel im Eimer ist.

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()