Peter Bucher - Mein Experiment, meine Spielereien, meine Welt...   ·   Stefan Falz   ·   Jürgen Gutsch   ·   Golo Roden   ·   ASP.NET Zone   ·   Microsoft ASP.NET
Willkommen bei ASP.NET Zone. Anmelden | Registrieren | Hilfe

Fehler verstecken leicht gemacht

Mit Try / Catch / Finally können in .NET Fehler behandelt werden.
Eigentlich eine gute Sache, allerdings sollte man aufpassen wo und wie man Fehler behandelt.

Vielerorts kann gelesen werden, dass das Fangen einer Allgemeinen Exception nicht gut sei, aber wieso ist das so?

Da ich es auch schon selber praktisch mehrmals erlebt habe, was das für schlimme Auswirkungen haben kann, ist es für mich nicht so schwer, diese Frage zu beantworten.

Ich möchte dies anhand eines kleinen, nachvollziehbaren Beispiels erläutern.

Gegeben ist folgender Code:


protected void Page_Load(object sender, EventArgs e)
{
    try
    {
        this.GridView1.DataSource = <DataSource>;
        this.GridView1.RowDataBound += GridView1_RowDataBound;
        CommandField deleteField = new CommandField
                   {
                     ShowDeleteButton = !this._recordsAreReadOnly,
                     DeleteText = “Löschen”,
                     DeleteImageUrl = "delete.png",
                     ButtonType = ButtonType.Image
                   };

         GridView1.Columns.Add(deleteField);
        // zusätzlichen Code…
        this.GridView.DataBind();
    }
    catch(Exception ex)
    {
        this.Logger.Log(ex);
    }
}
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if(e.Row.RowType == DataControlRowType.DataRow)
    {
        // zusätzlicher Code…
        ImageButton deleteButton = e.Row.FindControlRecursive<ImageButton>(c => c.CommandName == “Delete”);
        deleteButton.CssClass = “button_delete”;
        // zusätzlicher Code…
    }
}

Dieser Code, läuft - wie er jetzt da steht - ohne Probleme, solange die Daten nie als readonly gekennzeichnet werden.
Wird jetzt allerdings der Code erweitert, gibt es ein höchst merkwürdiges Verhalten, das ich mir zuerst überhaupt nicht erkären konnte.

Folgende Änderung:


ShowDeleteButton = !this._recordsAreReadOnly,

wird zu:


ShowDeleteButton = !this._recordsAreReadOnly && <Sicherheitsabfrage ob gelöscht werden darf>,

Der erste Teil der Bedingung liefert mit der Negation schlussendlich true und die Sicherheitsabfrage false, was ausgewertet dann einem false entspricht.
Somit werden die Löschen-Buttons nicht dargestellt. Gut. Eigentlich genau das was ich wollte.

Jedoch – und jetzt kommt der Haken – wird jetzt nur noch ein Datensatz im GridView angezeigt, anstelle von den zwei die in der Datenquelle vorhanden sind.

Im ersten Moment kam mir das ziemlich merkwürdig vor, da ich auch nirgendwo nochmals auf die Bedingung zugreifen und vor allem, weil auch kein Fehler geworfen wurde.

Kurz nachdem ich den Debugger angeworfen und kurz in den EventHandler “GridView1_RowDataBound” reingeschaut habe, kam es mir in den Sinn:

Böses Try / Catch, ein Fehler wurde versteckt, ohne das ich es gemerkt habe.

Was ist denn genau passiert?

Nun, da über die komplette Page_Load-Methode ein Try / Catch Konstrukt gespannt ist, das generell alle Fehler abfängt (Nicht behandelt, sondern eben “verschluckt”), bekomme ich den Fehler nicht zu Gesicht und der Code läuft in einem inkonsistenten Zustand weiter.

Der Eventhandler wird angemeldet und ein GridView1.DataBind()-Aufruf läuft im Eventhandler selber, also auch im Scope von besagtem Try / Catch, sodass die NullReferenceException, die eigentlich geworfen werden sollte, verschluckt wird.

Das hat dann dazu geführt, dass das GridView “irgendwie” noch halb fertig gerendert wird und man nichts vom Fehler mitbekommt, sondern eben nur einen Datensatz halb fertig gerendert wird.

Ich hoffe das dieses Beispiel ein wenig Klarheit bringt und vor allem euch aufweckt, Exception Handling mit Bedacht einzusetzen und nicht an einem solch komischen Verhalten zu verzweifeln.

Zusatz / Fazit:
Wie man sehen kann, ist der Scope (Wirkungsbereich) von Try / Catch um die ganze Page_Load-Methode gelegt.
Dies kann Sinn machen, jedoch nicht in der Mehrzahl der Fälle, wie auch in diesem Fall.

Meiner Meinung nach, und so steht es auch in den meisten Büchern, sollten Fehler punktuell abgefangen werden, nur dort wo man ihn auch behandeln kann und der Scope sollte möglichst klein gehalten werden, damit das Problem auch schnell identifiziert ist.

Die unbehandelten Fehler kann man dann generell in ASP.NET bspw. in der Global.asax.cs in der Methode Application_Error() loggen.
Ich persönlich gehe sogar so vor, das ich zuerst ohne Fehlerbehandlung entwickle und diese erst später hinzufüge.
So gehen während der Entwicklung keine Fehler vergessen und das Programm befindet sich nie in einem inkonsistenten Zustand.

Solchen Code wie oben gezeigt ist - wie auch Gregor im Kommentar bemerkt hat - vielerorts zu finden, was ich tragisch finde.
Die Konsequenzen daraus müssen in keinem Fall so harmlos sein, wie in diesem Beispiel gezeigt. Man stelle sich nur mal ein Atomkraftwerk vor, das durch eine inkonsistente Software gesteuert wird.

Bearbeitung / Korrekturen:
13.11.09 - Fehlendes Komma in den Codesnippets hinzugefügt
13.11.09 - Zusatz / Fazit hinzugefügt

Veröffentlicht Freitag, 13. November 2009 14:52 von Peter Bucher

Kommentare

# Social comments and analytics for this post

This post was mentioned on Twitter by peterbucher: blogged http://bit.ly/V7m3L - Fehler verstecken leicht gemacht

Freitag, 13. November 2009 15:32 by uberVU - social comments

# re: Fehler verstecken leicht gemacht

Hallo Peter,

einen betrachtlichen und zudem noch sehr wichtigen Artikel hast du geschrieben! Leider wird die Fehlerbehandlung unter .NET sehr vernachlässigt. Es wird häufig sogar mit Absicht ein Try-Catch-Block auf oberster Ebene gesetzt, damit dem Anwender später keine Meldung entgegen kommt und die Anwendung weiterhin läuft ohne abzustürzen. Nicht nur das, sondern ich finde fast kein Projekt wo nicht direkt „catch(Exception ex)“ verwendet wird. Das ist sehr Schlimm!

Auch gerade aus dem Problem was dir passiert ist sollte es dringend vermieden werden von Exception direkt abzufangen. Das sagen nicht nur wir beide sondern auch die .NET Design Guidelines von Microsoft.

Viele Grüße,

Gregor

Freitag, 13. November 2009 15:36 by BFreakout

# re: Fehler verstecken leicht gemacht

Hallo Peter,

ich stimme dir da vollkommen zu. catch(Exception ex) ist so ziemlich das übelste das man anstellen kann :-)

Mit diesem Construct ist dir echt ein abschreckendes Beispiel gelungen.

Vielleicht zeigst du in einem zweiten Artikel wie es richtig geht, quasi als Aufklärung: so nicht, sondern so.

Alles in allem ein guter Artikel, der bestimmt den ein oder anderen wach rüttelt.

Servus,

Klaus

Freitag, 13. November 2009 15:42 by klaus_b

# re: Fehler verstecken leicht gemacht

Hallo zusammen

Danke für eure Kommentare.

Ich habe noch einen kleinen Zusatz / Fazit hinzugefügt.

@Klaus

Entweder mache ich das, oder Jürgen, der hatte nämlich schon länger die Idee dazu.

Grüsse aus Konstanz, Peter

Freitag, 13. November 2009 15:53 by Peter Bucher

# re: Fehler verstecken leicht gemacht

Hallo Peter,

du hast folgendes geschrieben: "Ich persönlich gehe sogar so vor, das ich zuerst ohne Fehlerbehandlung entwickle und diese erst später hinzufüge."

Bei der Fehlerbehandlung handelt es sich um eine nicht-funktionale Anforderung und kann somit prima nachträglich mittels AOP (Aspektorientierter Programmierung) und einem Exception Handler (ich bevorzuge den Exception Application Block von Microsoft) behandelt werden.

Ich hab mit dem vorgehen eine sehr gute Erfahrung gemacht und kann es auch jedem aufs Herz legen.

Viele Grüße,

Gregor

Freitag, 13. November 2009 16:04 by BFreakout

# re: Fehler verstecken leicht gemacht

hi,

ich wäre an einem Artikel, der korrektes Exception-Handling aufzeigt sehr interessiert. Ich bin bisher über diverse Gerüchte wie "Code in try/catch wird langsamer ausgeführt" gestolpert. Das sind Gerüchte, die mich verunsichern, weshalb ich Exception-Handling bisher meide und als Ergebnis daraus genügend Routinen implementiere, die auf möglichst viele mögliche/potentielle Fehler prüfen. Es gibt jedoch auch Stellen, in denen das u.U. schwierig wird und ich mir eine andere Lösung wünschen würde...

mfg xforfun

Montag, 25. Januar 2010 08:49 by xforfun

# re: Fehler verstecken leicht gemacht

Hoi xforfun

Wenn es mir Recht ist wollte Jürgen zu diesem Thema noch etwas schreiben, ich kläre das mal mit ihm ab.

Es wird auf jeden Fall noch etwas kommen.

Exceptions sind langsam, allerdings nur wenn sie "missbraucht" werden.

Denn eigentlich sind das - wie der Name schon sagt - Ausnahmen.

Viel kann ohne Exceptions gelöst werden, Geschwindigkeitsprobleme bekommst du nur, wenn hunderte Exceptions fliegen und wieder aufgefangen werden, obwohl es gar nicht nötig wäre.

In wenigen Fällen programmierst du extra auf eine Exception (Bspw. SecurityException die du wieder abfängst). Allerdings sollte das die Ausnahme bleiben.

Bei den nicht abgefangenen / behandelten Exceptions bricht die Software sowieso ab, dann spielt Geschwindigkeit keine Rolle mehr.

Bei richtiger Benutzung ist es also ein Märchen, das Exceptions langsam sind.

Dienstag, 26. Januar 2010 07:42 by Peter Bucher
Anonyme Kommentare sind nicht zugelassen