Ogni volta che devo fornire ulteriori informazioni su un'eccezione, mi chiedo quale sia il modo giusto per farlo.
Per il bene di questa domanda ho scritto un esempio. Supponiamo che esista una classe in cui vogliamo aggiornare la Abbreviation
proprietà. Dal punto di vista SOLIDO potrebbe non essere perfetto, ma anche se passassimo il metodo worker tramite DI con qualche servizio si verificherebbe la stessa situazione - si verifica un'eccezione e non vi è alcun contesto. Torna all'esempio ...
class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Abbreviation { get; set; }
}
Quindi ci sono alcune istanze della classe e un ciclo in cui viene chiamato il metodo worker. Può lanciare il 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
}
La domanda è: come aggiungere la Person
o la sua Id
(o qualsiasi altra cosa)?
Conosco le tre tecniche seguenti:
1 - Usa la Data
proprietà
Professionisti:
- facile impostare ulteriori informazioni
- non richiede la creazione di ulteriori eccezioni
- non richiede ulteriori
try/catch
Contro:
- non può essere facilmente integrato in
Message
- i logger ignorano questo campo e non lo scaricheranno
- richiede chiavi e casting perché i valori sono
object
- non immutabile
Esempio:
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 - Usa proprietà personalizzate
Professionisti:
- simile alla
Data
proprietà ma fortemente tipizzato - più facile da integrare in
Message
Contro:
- richiede eccezioni personalizzate
- il logger li ignorerà
- non immutabile
Esempio:
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 - Avvolgere l'eccezione con un'altra eccezione
Professionisti:
Message
può essere formattato in modo prevedibile- i logger scaricheranno eccezioni interne
- immutabile
Contro:
- richiede ulteriore
try/catch
- aumenta la nidificazione
- aumenta la profondità delle eccezioni
Esempio:
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...
}
- Ci sono altri schemi?
- Ci sono schemi migliori?
- Puoi suggerire le migliori pratiche per qualcuno / tutti?