Version 2
14.07.2010 04:19:33
Dies ist die aktuelle Version
Und hier meine Antworten:
Exceptions sollten eigendlich immer nur dann behandelt werden wenn du Sie erwartest und behandeln kannst. In deinem Beispiel mit der Datenbankverbindung, "Datenbankverbindung fehlgeschlagen", kannst du weder im "Provider Layer" noch im
"Data Access Layer" oder im "Business Layer" eine sinnvolle Behandlung des Fehlers schreiben. Lediglich der "Presentation Layer" kann hier etwas machen, und zwar in erste Linie den Benutzer darüber informieren, ihn bitten seine Netzwerkverbindung zu überprüfen etc.
Zudem wirken sich zu viele Try-Catches negativ auf die Laufzeit aus.
Damit ist Frage 4 "4) Wann setze ich try/catch Blöcke sinnvoll ein?" und Frage 1 "1) Ist meine Art der Fehlerbehandlung (Argumente überprüfen, try/catch Block) in Ordnung?" eigendlich schon beantwortet.
Frage 1: nein - Try-Catch nur dann verwenden wenn du auch etwas tun kannst.
Frage 4: Immer dann wenn du etwas tun kannst :D
2) Sollte man anstatt
if(object != null) { ... }
lieber
if(object == null) throw new Exception("...")
werfen?
Ja, auf jeden Fall. Noch besser wäre aber keine generische Excpetion zu nehmen sondern eine Exceptionklasse die von "Exception" abgeleitet ist oder selber Ableitung schreiben wenn keine fertige Klasse vorhanden ist.
if(object == null)
throw new ArgumentNullReferenzException("Der Parameter 'object' darf nicht 'null' sein.")
3) Wann gebe ich Fehler bis an die Oberfläche weiter (kritische: Datenbankverbindung fehlgeschlagen)?
In der Regel immer dann, wenn weder der "Provider Layer" noch der
"Data Access Layer" oder der "Business Layer" eine sinnvolle Behandlung des Fehlers ermöglichen.
Ein Fehler den der "Business Layer" behandeln könnte wäre zum Beispiel eine FileNotFoundException die ausgelößt wird weil du versucht beim Programmstart deine Einstellungsdatei für den Benutzer zu laden. In diesem Fall würdest du so vorgehen:
public void LoadSettingsInContext(User currentUser){
try{
Context.Load(Path.Combine(App.CurrentPath,currentUser.Name + ".settings"))
}
catch (FileNotFoundException ex){
Content.CreateSettingsFileWithDefaultValues(Path.Combine(App.CurrentPath,currentUser.Name + ".settings"))
}
}Wie du siehst ist eine Behandlung in diesem Fall möglich da das Settingsfile, sollte es fehlen, mit den Defaultwerten erneut erzeugt wird.
Eine "SecurityException" die aussagt das du nicht Zugriffsberechtigt auf diese Datei oder Verzeichnis bist, ist nicht behandelbar (es sei denn du schreibst nen Hack für Windows um dir mehr Rechte zu verschaffen um dann auf die Datei zuzugreifen :D).
Hier musst du also den User Informieren das er sich um das Problem kümmern oder seinen Administrator zu sich bestellen soll.
Ich hoffe ich konnte den Unterschied gut verständlich darlegen.
Edit: Noch eine Anmerkung!
Wenn du in dein Log den komplatten Stacktrace schreibst kannst du genau erkennen wo das Problem aufgetreten ist.
//Wann, Was, Wo - Text
2009-07-20 15:34:03 [ERROR] [DatenImport.JobClass] - Fehler im Importer
//Die Fehlermeldung .. in diesem Fall "SqlException" und der Fehlertext
System.Data.SqlClient.SqlException: Bei der Konvertierung eines char-Datentyps in einen datetime-Datentyp liegt der datetime-Wert außerhalb des gültigen Bereichs.
Die Anweisung wurde beendet.
//Interne Klassen vom .Net-Framework .. machmal sehr hilfreich bei der Suche nach dem Fehler
// Aber ah: hier ist der Fehler, also die Exception, ausgelößt worden.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
at System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
at System.Data.SqlClient.SqlDataReader.get_MetaData()
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteScalar()
//Oh .. hier wirds interessant:
//Der Fehler ist in der Funktion "InternalImport" in der Class "XXXImporter" im Namespace "Mail.DatenImport.Importer" in der Zeile 130 zwar nicht aufgetreten, aber das ist mein Quellcode, ich hätte hier das erste mal die Möglichkeit gehabt den Fehler zu behandeln.
at Mail.DatenImport.Importer.XXXImporter.InternalImport(Stream stream) in D:\ProjectX\Importer\XXXImporter.cs:line 130
at Mail.DatenImport.Importer.XXXImporter.Import(Stream stream) in D:\ProjectX\Importer\XXXImporter.cs:line 22
at Mail.DatenImport.JobClass.Parse_AttachmentList(Pop3Message Message, Guid interne_MessageID, List`1 FileExtensionList, IImporter Importer) in D:\ProjectX\JobClass.cs:line 140
//Schluss.. Denn an dieser stelle habe ich den Fehler behandelt, ins Log geschrieben, mir als Programmierer eine E-Mail geschickt und mit dem nächsten Schleifendurchlauf weiter gemacht.