Come devo fornire ulteriori informazioni su un'eccezione?


20

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 Abbreviationproprietà. 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 Persono la sua Id(o qualsiasi altra cosa)?


Conosco le tre tecniche seguenti:


1 - Usa la Dataproprietà

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 Dataproprietà 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?

Non ho familiarità con le eccezioni in C # ma normalmente mi aspetto che l'istanza Person sia ancora valida quando viene generata l'eccezione. Ci hai provato?
John Kouraklis,

1
@JohnKouraklis non è questo il problema ;-) È solo un esempio estremamente semplice per dimostrare cosa intendo con ulteriori informazioni. Se avessi pubblicato qui un intero framework in cui i metodi multipli possono generare eccezioni e dovrebbero essere forniti livelli multipli di informazioni sul contesto, nessuno probabilmente leggerà questo e ho avuto davvero difficoltà a spiegarlo.
t3chb0t

@JohnKouraklis L'ho appena inventato a scopo dimostrativo.
t3chb0t

@ t3chb0t Penso che tu abbia risposto alla tua domanda qui. Considera di spostare 1, 2 e 3 in una risposta e di adattare la tua domanda in modo da non chiedermi di scegliere uno stile in base alla mia opinione.
candied_orange

Cosa c'è che non va nelle eccezioni personalizzate? Fatto correttamente, fanno parte del linguaggio del tuo dominio e aiutano a raggiungere l'astrazione lontano dai dettagli di implementazione.
RubberDuck,

Risposte:


6

Data FTW .

Il tuo "contra":

  • "non può essere facilmente integrato nel messaggio"

-> Per i vostri tipi di eccezione, dovrebbe essere abbastanza facile da ignorare Messagein modo che non incorporano Data.. anche se solo considero questo se l' Dataè il messaggio .

  • "i logger ignorano questo campo e non lo scaricano"

Googling per Nlog come esempio produce :

Rendering del layout delle eccezioni

(...)

format - Formato dell'output. Deve essere un elenco separato da virgole di proprietà di eccezione: Message, Type, ShortType, ToString, Method, StackTracee Data. Questo valore di parametro non fa distinzione tra maiuscole e minuscole. Predefinito:message

Quindi sembra che sia facilmente configurabile.

  • richiede chiavi e casting perché i valori sono oggetti

Eh? Scarica gli oggetti e assicurati che abbiano un ToString()metodo utilizzabile .

Inoltre, non vedo alcun problema con le chiavi. Usa solo un po 'di unicità e sei bravo.


Disclaimer: questo è quello che ho potuto vedere immediatamente dalla domanda e su cosa ho cercato su Google Datain 15 minuti. Ho pensato che fosse leggermente utile, quindi l'ho messo come una risposta, ma non mi sono mai usato Data, quindi potrebbe essere che l'interrogante qui sappia molto più di questo di me.


Sono giunto alla conclusione che ci sono solo due cose su un'eccezione che sono utili, il suo nome e messaggio. Tutto il resto è solo un rumore inutile che può e deve essere ignorato perché è semplicemente troppo fragile fare affidamento su di esso.
t3chb0t,

2

Perché lanci eccezioni? Per averli catturati e gestiti.

Come funziona il codice catching come gestire l'eccezione? Utilizzo delle proprietà definite nell'oggetto Exception.

Non utilizzare mai la proprietà Message per identificare l'eccezione, né per fornire "informazioni" sulle quali ogni potenziale gestore dovrebbe fare affidamento. È semplicemente troppo volatile e inaffidabile.

Non ho mai usato la proprietà "Dati" prima, ma mi sembra troppo generico.

A meno che non si creino molte classi di Eccezione, ognuna delle quali identifica un caso Eccezionale specifico , come si fa a sapere quando si rileva l'eccezione quali "Dati" rappresentano? (Vedi il commento precedente su "Messaggio").


1
Direi che Dataè inutile per la gestione, ma prezioso per la registrazione per evitare di Messageformattare l'inferno.
Martin Ba,

-1

Mi piace il tuo terzo esempio, tuttavia esiste un altro modo in cui può essere codificato per eliminare la maggior parte dei tuoi "trucchi".

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    var exceptions = new List<InvalidPersonDataException>();

    foreach (var person in persons)
    {
        try
        {
            person.Abbreviation = GenerateAbbreviation(person.Name);
        }
        catch(Exception ex)
        {
            exceptions.Add(new InvalidPersonDataException(person.Id, ex));
        }
    }

    if (exceptions.Any())
    {
        throw new AggregateException(exceptions);
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.