Come dovrei fornire ulteriori informazioni su un'eccezione?

12

Ogni volta che ho bisogno di fornire informazioni aggiuntive su un'eccezione mi chiedo quale sia in realtà il modo giusto per farlo.

Per amore di questa domanda ho scritto un esempio. Supponiamo che esista una classe in cui vogliamo aggiornare la proprietà Abbreviation . Dal punto di vista SOLIDO, potrebbe non essere perfetto, ma anche se abbiamo passato il metodo-lavoratore via DI con qualche servizio, si verificherebbe la stessa situazione - si verifica un'eccezione e non c'è un contesto. Torna all'esempio ...

class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Abbreviation { get; set; }
}

Poi ci sono alcune istanze della classe e un ciclo in cui viene chiamato il metodo worker. Può lanciare 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 il Person o il suo Id (o qualsiasi altra cosa)?

Conosco le seguenti tre tecniche:

1: utilizza la proprietà Data

Pro:

  • facile da impostare ulteriori informazioni
  • non richiede la creazione di ulteriori eccezioni
  • non richiede ulteriore 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: utilizza le proprietà personalizzate

Pro:

  • simile alla proprietà Data ma strongmente tipizzata
  • più facile da integrare in Message

Contro:

  • richiede eccezioni personalizzate
  • 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: avvolge l'eccezione con un'altra eccezione

Pro:

  • Message può essere formattato in modo prevedibile
  • I logger
  • eseguiranno il dump delle eccezioni interne
  • immutabile

Contro:

  • richiede un ulteriore try/catch
  • aumenta la nidificazione
  • aumenta la profondità delle espressioni

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?
  • Esistono modelli migliori?
  • Puoi suggerire le best practice per tutti / tutti?
posta t3chb0t 19.11.2016 - 21:53
fonte

3 risposte

5

Data FTW .

Il tuo "contra":

  • "non può essere facilmente integrato nel messaggio"

- > Per i tuoi tipi di eccezioni, dovrebbe essere abbastanza semplice sovrascrivere Message in modo che faccia incorpori Data .. anche se lo considererei solo se Data è il messaggio .

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

Cerca su Google come esempio rendimento :

Exception layout renderer

(...)

format - Format of the output. Must be a comma-separated list of exception properties: Message, Type, ShortType, ToString, Method, StackTrace & Data. This parameter value is case-insensitive. Default: message

Quindi sembra che sia facilmente configurabile.

  • richiede chiavi e casting perché i valori sono oggetto

Eh? Esegui semplicemente il dump degli oggetti e assicurati che abbiano un metodo ToString() utilizzabile.

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

Dichiarazione di non responsabilità: questo è ciò che ho potuto immediatamente vedere dalla domanda e cosa ho cercato su Data in 15 minuti. Ho pensato che fosse leggermente utile, quindi l'ho messo come risposta, ma non ho mai usato Data me stesso, quindi potrebbe essere che l'interrogante qui sappia molto di più di questo.

    
risposta data 23.11.2016 - 22:38
fonte
2

Perché butti delle eccezioni? Per averli catturati e maneggiati.

In che modo il codice catching emette come per gestire l'eccezione? Utilizzando le proprietà che si definiscono sull'oggetto Exception.

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

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

A meno che non crei molte classi di Eccezione, ognuna delle quali identifica un caso Eccezionale specifico , come fai a sapere quando rilevi l'Eccezione cosa rappresenta "Data"? (Vedi il commento precedente su "Messaggio").

    
risposta data 24.11.2016 - 13:16
fonte
-1

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

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);
    }
}
    
risposta data 02.12.2016 - 16:06
fonte

Leggi altre domande sui tag