Chaque fois que je dois fournir des informations supplémentaires sur une exception, je me demande quelle est la bonne façon de procéder.
Pour cette question, j'ai écrit un exemple. Supposons qu'il y ait une classe où nous voulons mettre à jour leAbbreviation
propriété. Du point de vue SOLIDE, cela pourrait ne pas être parfait, mais même si nous passions la méthode de travail via DI avec un certain service, la même situation se produirait - une exception se produit et il n'y a pas de contexte. Retour à l'exemple ...
class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Abbreviation { get; set; }
}
Ensuite, il y a quelques instances de la classe et une boucle où la méthode de travail est appelée. Il peut jeter leStringTooShortException
.
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 question est: comment ajouter le Person
ou son Id
(ou autre chose)?
Je connais les trois techniques suivantes:
1 - Utiliser la Data
propriété
Avantages:
- informations supplémentaires faciles à définir
- ne nécessite pas de créer encore plus d'exceptions
- ne nécessite pas de supplément
try/catch
Les inconvénients:
- ne peut pas être facilement intégré dans le
Message
- les bûcherons ignorent ce champ et ne le jettent pas
- nécessite des clés et une conversion car les valeurs sont
object
- non immuable
Exemple:
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 - Utiliser des propriétés personnalisées
Avantages:
- similaire à la
Data
propriété mais fortement typé - plus facile à intégrer dans le
Message
Les inconvénients:
- nécessite des exceptions personnalisées
- l'enregistreur les ignorera
- non immuable
Exemple:
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 - Envelopper l'exception avec une autre exception
Avantages:
Message
peut être formaté de manière prévisible- les enregistreurs déchargent les exceptions internes
- immuable
Les inconvénients:
- nécessite supplémentaire
try/catch
- nidification increses
- augmente la profondeur des exonérations
Exemple:
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...
}
- Existe-t-il d'autres modèles?
- Y a-t-il de meilleurs modèles?
- Pouvez-vous suggérer les meilleures pratiques pour chacun d'entre eux?