Jedes Mal, wenn ich zusätzliche Informationen zu einer Ausnahme bereitstellen muss, frage ich mich, auf welche Weise dies tatsächlich richtig ist .
Um dieser Frage willen habe ich ein Beispiel geschrieben. Angenommen, es gibt eine Klasse, in der die Abbreviation
Eigenschaft aktualisiert werden soll . Aus der Sicht von SOLID ist es vielleicht nicht perfekt, aber selbst wenn wir die Worker-Methode über DI mit einem Service übergeben würden, würde die gleiche Situation eintreten - eine Ausnahme tritt auf und es gibt keinen Kontext dazu. Zurück zum Beispiel ...
class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Abbreviation { get; set; }
}
Dann gibt es einige Instanzen der Klasse und eine Schleife, in der die Worker-Methode aufgerufen wird. Es kann das werfen StringTooShortException
.
var persons =
{
new Person { Id = 1, Name = "Fo" },
new Person { Id = 2, Name = "Barbaz" },
}
public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
foreach (var person in persons)
{
try
{
person.Abbreviation = GenerateAbbreviation(person.Name);
}
catch(Exception ex)
{
// ?
}
}
// throw AggregateException...
}
public IEnumerable<string> GenerateAbbreviation(string value)
{
if (value.Length < 5)
{
throw new StringTooShortException(value);
}
// generate abbreviation
}
Die Frage ist: Wie fügt man das Person
oder sein Id
(oder irgendetwas anderes) hinzu?
Ich kenne die folgenden drei Techniken:
1 - Verwenden Sie die Data
Eigenschaft
Vorteile:
- einfach, zusätzliche Informationen einzustellen
- Es müssen keine weiteren Ausnahmen erstellt werden
- erfordert keine zusätzlichen
try/catch
Nachteile:
- kann nicht einfach in die integriert werden
Message
- Logger ignorieren dieses Feld und geben es nicht aus
- benötigt Schlüssel und Casting weil Werte sind
object
- nicht unveränderlich
Beispiel:
public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
foreach (var person in persons)
{
try
{
person.Abbreviation = GenerateAbbreviation(person.Name);
}
catch(Exception ex)
{
ex.Data["PersonId"] = person.Id;
// collect ex
}
}
// throw AggregateException...
}
2 - Verwenden Sie benutzerdefinierte Eigenschaften
Vorteile:
- ähnlich wie das
Data
Eigentum, aber stark getippt - einfacher zu integrieren in die
Message
Nachteile:
- erfordert benutzerdefinierte Ausnahmen
- Logger ignoriert sie
- nicht unveränderlich
Beispiel:
public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
foreach (var person in persons)
{
try
{
person.Abbreviation = GenerateAbbreviation(person.Name);
}
catch(Exception ex)
{
// not suitable for this exception because
// it doesn't have anything in common with the Person
}
}
// throw AggregateException...
}
3 - Schließen Sie die Ausnahme mit einer anderen Ausnahme ab
Vorteile:
Message
kann vorhersehbar formatiert werden- Logger geben innere Ausnahmen aus
- unveränderlich
Nachteile:
- erfordert zusätzliche
try/catch
- Inkrementelle Verschachtelung
- erhöht die Tiefe der Ausnahmen
Beispiel:
public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
foreach (var person in persons)
{
try
{
try
{
person.Abbreviation = GenerateAbbreviation(person.Name);
}
catch(Exception ex)
{
throw new InvalidPersonDataException(person.Id, ex);
}
}
catch(Exception ex)
{
// collect ex
}
}
// throw AggregateException...
}
- Gibt es noch andere Muster?
- Gibt es bessere Muster?
- Können Sie für alle Best Practices vorschlagen?