Sempre que preciso fornecer informações adicionais sobre uma exceção, pergunto-me qual é a maneira correta de fazer isso.
Para o bem desta pergunta, escrevi um exemplo. Vamos supor que há uma classe em que queremos atualizar a Abbreviation
propriedade. Do ponto de vista do SOLID, pode não ser perfeito, mas mesmo que passássemos o método de trabalho via DI com algum serviço, a mesma situação ocorreria - ocorre uma exceção e não há contexto para ele. Voltar ao exemplo ...
class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Abbreviation { get; set; }
}
Depois, existem algumas instâncias da classe e um loop em que o método worker é chamado. Pode jogar o 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
}
A questão é: como adicionar o Person
ou seu Id
(ou qualquer outra coisa)?
Conheço as três técnicas a seguir:
1 - Use a Data
propriedade
Prós:
- fácil de configurar informações adicionais
- não requer a criação de ainda mais exceções
- não requer adicional
try/catch
Contras:
- não pode ser facilmente integrado ao
Message
- os madeireiros ignoram esse campo e não o despejam
- requer chaves e elenco porque os valores são
object
- não imutável
Exemplo:
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 - Use propriedades personalizadas
Prós:
- semelhante à
Data
propriedade, mas fortemente tipado - mais fácil de integrar no
Message
Contras:
- requer exceções personalizadas
- logger irá ignorá-los
- não imutável
Exemplo:
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 - Quebra a exceção com outra exceção
Prós:
Message
pode ser formatado de forma previsível- madeireiros irão despejar exceções internas
- imutável
Contras:
- requer adicional
try/catch
- increses aninhamento
- aumenta a profundidade das exceções
Exemplo:
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...
}
- Existem outros padrões?
- Existem padrões melhores?
- Você pode sugerir práticas recomendadas para algumas delas?
fonte
Respostas:
Data
FTW .O seu "contra":
-> Para seus tipos de exceção, deve ser fácil substituí-
Message
lo para que ele seja incorporadoData
.. embora eu só considere isso se essaData
for a mensagem .Pesquisando para NLog como exemplo rendimento :
Parece que é facilmente configurável.
Hã? Apenas despeje os objetos lá e verifique se eles têm um
ToString()
método utilizável .Além disso, não vejo nenhum problema com as chaves. Basta usar alguma singularidade leve e você é bom.
Isenção de responsabilidade: foi o que pude ver imediatamente na pergunta e o que pesquisei
Data
em 15 minutos. Eu pensei que era um pouco útil, então eu a coloco como resposta, mas nuncaData
me usei , então pode ser que o interlocutor aqui saiba muito mais sobre isso do que eu.fonte
Por que você lança exceções? Tê-los capturados e manuseados.
Como o código de captura funciona como lidar com a exceção? Usando as propriedades que você define no objeto Exception.
Nunca use a propriedade Message para identificar a exceção, nem para fornecer "informações" nas quais qualquer manipulador em potencial deve confiar. É simplesmente muito volátil e não confiável.
Eu nunca usei a propriedade "Data" antes, mas parece excessivamente genérica para mim.
A menos que você criar muitas classes de exceção, cada um dos quais identifica um determinado caso excepcional, como você sabe quando você capturar a exceção que "Data" representa? (Veja o comentário anterior sobre "Mensagem").
fonte
Data
é inútil para o manuseio, mas valioso para o log para evitar aMessage
formatação do inferno.Eu gosto do seu terceiro exemplo, no entanto, existe outra maneira de codificá-lo para eliminar a maioria dos seus "contras".
fonte