La gestione delle eccezioni

La gestione delle eccezioni

# Creazione di eccezioni personalizzate

È consentito implementare eccezioni personalizzate che possono essere generate come qualsiasi altra eccezione. Questo ha senso quando vuoi rendere le tue eccezioni distinguibili da altri errori durante il runtime.

In questo esempio creeremo un'eccezione personalizzata per una chiara gestione dei problemi che l'applicazione potrebbe avere durante l'analisi di un input complesso.

# Creazione di una classe di eccezione personalizzata

Per creare un'eccezione personalizzata, crea una sottoclasse di Exception :

public class ParserException : Exception
{
    public ParserException() : 
      base("The parsing went wrong and we have no additional information.") { }
}

L'eccezione personalizzata diventa molto utile quando vuoi fornire informazioni aggiuntive al ricevitore:

public class ParserException : Exception
{
    public ParserException(string fileName, int lineNumber) : 
      base($"Parser error in {fileName}:{lineNumber}") 
    {
      FileName = fileName;
      LineNumber = lineNumber;
    }
    public string FileName {get; private set;}
    public int LineNumber {get; private set;}    
}

Ora, quando catch(ParserException x) avrai una semantica aggiuntiva per ottimizzare la gestione delle eccezioni.

Le classi personalizzate possono implementare le seguenti funzionalità per supportare scenari aggiuntivi.

# rilancio

Durante il processo di analisi, l'eccezione originale è ancora interessante. In questo esempio è un FormatException perché il codice tenta di analizzare un pezzo di stringa, che dovrebbe essere un numero. In questo caso l'eccezione personalizzata dovrebbe supportare l'inclusione di 'InnerException ':

//new constructor:
ParserException(string msg, Exception inner) : base(msg, inner) {
}

# serializzazione

In alcuni casi le tue eccezioni potrebbero dover superare i limiti di AppDomain. Questo è il caso se il tuo parser è in esecuzione nel proprio AppDomain per supportare il ricaricamento a caldo di nuove configurazioni del parser. In Visual Studio puoi usare Exception modello per generare codice come questo.

[Serializable]
public class ParserException : Exception
{
    // Constructor without arguments allows throwing your exception without
    // providing any information, including error message. Should be included
    // if your exception is meaningful without any additional details. Should
    // set message by calling base constructor (default message is not helpful).
    public ParserException()
        : base("Parser failure.")
    {}

    // Constructor with message argument allows overriding default error message.
    // Should be included if users can provide more helpful messages than
    // generic automatically generated messages.
    public ParserException(string message) 
        : base(message)
    {}

    // Constructor for serialization support. If your exception contains custom
    // properties, read their values here.
    protected ParserException(SerializationInfo info, StreamingContext context) 
        : base(info, context)
    {}
}

# Utilizzo di ParserException

try
{
    Process.StartRun(fileName)
}
catch (ParserException ex)
{
    Console.WriteLine($"{ex.Message} in ${ex.FileName}:${ex.LineNumber}");
}
catch (PostProcessException x) 
{
    ...
}

Puoi anche utilizzare eccezioni personalizzate per catturare e racchiudere le eccezioni. In questo modo molti errori diversi possono essere convertiti in un unico tipo di errore più utile per l'applicazione:

try
{
    int foo = int.Parse(token);
}
catch (FormatException ex)
{
    //Assuming you added this constructor
    throw new ParserException(
      $"Failed to read {token} as number.", 
      FileName, 
      LineNumber, 
      ex);
}

Quando gestisci le eccezioni sollevando le tue eccezioni personalizzate, dovresti generalmente includere un riferimento all'eccezione originale nel InnerException proprietà, come mostrato sopra.

# problemi di sicurezza

Se esporre il motivo dell'eccezione potrebbe compromettere la sicurezza consentendo agli utenti di vedere il funzionamento interno dell'applicazione, può essere una cattiva idea racchiudere l'eccezione interna. Ciò potrebbe applicarsi se stai creando una libreria di classi che verrà utilizzata da altri.

Ecco come puoi sollevare un'eccezione personalizzata senza eseguire il wrapping dell'eccezione interna:

try
{
  // ...
}
catch (SomeStandardException ex)
{
  // ...
  throw new MyCustomException(someMessage);
}

# Conclusione

Quando si solleva un'eccezione personalizzata (con il wrapping o con una nuova eccezione annullata), è necessario sollevare un'eccezione significativa per il chiamante. Ad esempio, un utente di una libreria di classi potrebbe non sapere molto su come quella libreria svolge il suo lavoro interno. Le eccezioni generate dalle dipendenze della libreria di classi non sono significative. Piuttosto, l'utente desidera un'eccezione che sia rilevante per il modo in cui la libreria di classi utilizza quelle dipendenze in modo errato.

try
{
  // ...
}
catch (IOException ex)
{
  // ...
  throw new StorageServiceException(@"The Storage Service encountered a problem saving
your data. Please consult the inner exception for technical details. 
If you are not able to resolve the problem, please call 555-555-1234 for technical       
assistance.", ex);
}

# Finalmente blocca

try
{
    /* code that could throw an exception */
}
catch (Exception)
{
    /* handle the exception */
}
finally
{
    /* Code that will be executed, regardless if an exception was thrown / caught or not */
}

Il try / catch / finally block può essere molto utile durante la lettura da file.
Ad esempio:

FileStream f = null;

try
{
    f = File.OpenRead("file.txt");
    /* process the file here */
}
finally
{
    f?.Close(); // f may be null, so use the null conditional operator.
}

Un blocco try deve essere seguito da un catch o un finally bloccare. Tuttavia, poiché non esiste un blocco catch, l'esecuzione causerà la terminazione. Prima della terminazione, verranno eseguite le istruzioni all'interno del blocco finally.

Nella lettura del file avremmo potuto usare un using blocca come FileStream (che cosa OpenRead restituisce) implementa IDisposable .

Anche se c'è un return dichiarazione in try blocco, il finally il blocco di solito viene eseguito; ci sono alcuni casi in cui non lo farà:

  • Quando si verifica uno StackOverflow.
  • Environment.FailFast
  • Il processo dell'applicazione viene terminato, di solito da una fonte esterna.

# Best practice

# Cheatsheet

FAI NON
Flusso di controllo con istruzioni di controllo Controlla il flusso con eccezioni
Tieni traccia dell'eccezione ignorata (assorbita) registrando Ignora eccezione
Ripeti l'eccezione utilizzando throw Rilancia eccezione - throw new ArgumentNullException() o throw ex
Genera eccezioni di sistema predefinite Crea eccezioni personalizzate simili alle eccezioni di sistema predefinite
Genera un'eccezione personalizzata/predefinita se è fondamentale per la logica dell'applicazione Genera eccezioni personalizzate/predefinite per indicare un avviso nel flusso
Cattura le eccezioni che vuoi gestire Cattura ogni eccezione

# NON gestire la logica aziendale con eccezioni.

Il controllo del flusso NON deve essere eseguito tramite eccezioni. Utilizzare invece le istruzioni condizionali. Se è possibile eseguire un controllo con if-else dichiarazione chiaramente, non utilizzare eccezioni perché riduce la leggibilità e le prestazioni.

Considera il seguente frammento di Mr. Bad Practices:

// This is a snippet example for DO NOT
object myObject;
void DoingSomethingWithMyObject()
{
    Console.WriteLine(myObject.ToString());
}

Quando l'esecuzione raggiunge Console.WriteLine(myObject.ToString()); l'applicazione genererà un'eccezione NullReferenceException. Mr. Bad Practices si è reso conto che myObject è nullo e ha modificato il suo snippet per catturare e gestire NullReferenceException :

// This is a snippet example for DO NOT
object myObject;
void DoingSomethingWithMyObject()
{
    try
    {
        Console.WriteLine(myObject.ToString());
    }
    catch(NullReferenceException ex)
    {
        // Hmmm, if I create a new instance of object and assign it to myObject:
        myObject = new object();
        // Nice, now I can continue to work with myObject
        DoSomethingElseWithMyObject();
    }
}

Poiché lo snippet precedente copre solo la logica dell'eccezione, cosa devo fare se myObject non è nullo a questo punto? Dove dovrei coprire questa parte della logica? Subito dopo Console.WriteLine(myObject.ToString()); ? Che ne dici dopo il try...catch bloccare?

Che ne dici di Mr. Best Practices? Come lo gestirebbe?

// This is a snippet example for DO
object myObject;
void DoingSomethingWithMyObject()
{
    if(myObject == null)
        myObject = new object();
    
    // When execution reaches this point, we are sure that myObject is not null
    DoSomethingElseWithMyObject();
}

Mr. Best Practices ha ottenuto la stessa logica con meno codice e una logica chiara e comprensibile.

# NON rilanciare le eccezioni

Rilanciare le eccezioni è costoso. Ha un impatto negativo sulle prestazioni. Per il codice che ha esito negativo di routine, è possibile utilizzare modelli di progettazione per ridurre al minimo i problemi di prestazioni. Questo argomento descrive due modelli di progettazione utili quando le eccezioni potrebbero influire in modo significativo sulle prestazioni.

# NON assorbire le eccezioni senza registrazione

try
{
    //Some code that might throw an exception
}
catch(Exception ex)
{
    //empty catch block, bad practice
}

Mai ingoiare eccezioni. Ignorare le eccezioni salverà quel momento ma creerà un caos per la manutenibilità in un secondo momento. Quando si registrano le eccezioni, è necessario registrare sempre l'istanza dell'eccezione in modo che venga registrata l'intera traccia dello stack e non solo il messaggio di eccezione.

try
{
    //Some code that might throw an exception
}
catch(NullException ex)
{
    LogManager.Log(ex.ToString());
}

# Non catturare eccezioni che non puoi gestire

Molte risorse, come questa, ti sollecitano fortemente a considerare il motivo per cui stai rilevando un'eccezione nel punto in cui la stai rilevando. Dovresti rilevare un'eccezione solo se riesci a gestirla in quella posizione. Se puoi fare qualcosa per mitigare il problema, come provare un algoritmo alternativo, connetterti a un database di backup, provare un altro nome file, aspettare 30 secondi e riprovare, o notificare un amministratore, puoi catturare l'errore e farlo. Se non c'è nulla che puoi fare in modo plausibile e ragionevole, "lascialo andare" e lascia che l'eccezione venga gestita a un livello superiore. Se l'eccezione è sufficientemente catastrofica e non c'è un'opzione ragionevole diversa dall'arresto anomalo dell'intero programma a causa della gravità del problema, fallo in modo anomalo.

try
{
    //Try to save the data to the main database.
}
catch(SqlException ex)
{
    //Try to save the data to the alternative database.
}
//If anything other than a SqlException is thrown, there is nothing we can do here. Let the exception bubble up to a level where it can be handled.

# Eccezione Anti-patterns

# Eccezioni di ingestione

Si dovrebbe sempre lanciare nuovamente l'eccezione nel modo seguente:

try
{
    ...
}
catch (Exception ex)
{
    ...
    throw;
}

Rilanciare un'eccezione come di seguito offuscherà l'eccezione originale e perderà la traccia dello stack originale. Non si dovrebbe mai farlo! La traccia dello stack prima del catch and rethrow andrà persa.

try
{
    ...
}
catch (Exception ex)
{
    ...
    throw ex;
}

# Gestione delle eccezioni di baseball

Non si dovrebbero usare eccezioni come sostituti dei normali costrutti di controllo del flusso come le istruzioni if-then e i cicli while. Questo anti-modello è talvolta chiamato Gestione delle eccezioni di baseball.

Ecco un esempio dell'anti-pattern:

try
{
    while (AccountManager.HasMoreAccounts())
    {
        account = AccountManager.GetNextAccount();
        if (account.Name == userName)
        {
            //We found it
            throw new AccountFoundException(account);
        }
    }
}
catch (AccountFoundException found)
{
    Console.Write("Here are your account details: " + found.Account.Details.ToString());
}

Ecco un modo migliore per farlo:

Account found = null;
while (AccountManager.HasMoreAccounts() && (found==null))
{
    account = AccountManager.GetNextAccount();
    if (account.Name == userName)
    {
        //We found it
        found = account;
    }
}
Console.Write("Here are your account details: " + found.Details.ToString());

# catch (eccezione)

Non ci sono quasi (alcuni dicono nessuno!) Motivi per catturare il tipo di eccezione generico nel tuo codice. Dovresti rilevare solo i tipi di eccezione che ti aspetti si verifichino, perché altrimenti nascondi i bug nel tuo codice.

try 
{
     var f = File.Open(myfile);
     // do something
}
catch (Exception x)
{
     // Assume file not found
     Console.Write("Could not open file");
     // but maybe the error was a NullReferenceException because of a bug in the file handling code?
}

Meglio fare:

try 
{
     var f = File.Open(myfile);
     // do something which should normally not throw exceptions
}
catch (IOException)
{
     Console.Write("File not found");
}
// Unfortunatelly, this one does not derive from the above, so declare separatelly
catch (UnauthorizedAccessException) 
{
     Console.Write("Insufficient rights");
}

Se si verifica un'altra eccezione, lasciamo di proposito l'arresto anomalo dell'applicazione, in modo che intervenga direttamente nel debugger e possiamo risolvere il problema. Non dobbiamo spedire un programma in cui si verificano comunque eccezioni diverse da queste, quindi non è un problema avere un arresto anomalo.

Anche il seguente è un cattivo esempio, perché usa le eccezioni per aggirare un errore di programmazione. Non è per questo che sono stati progettati.

public void DoSomething(String s)
{
     if (s == null)
         throw new ArgumentNullException(nameof(s));
     // Implementation goes here
}

try 
{    
     DoSomething(myString);
}
catch(ArgumentNullException x)
{
    // if this happens, we have a programming error and we should check
    // why myString was null in the first place.
}

# Gestione delle eccezioni di base

try
{
    /* code that could throw an exception */
}
catch (Exception ex)
{
    /* handle the exception */
}

Si noti che la gestione di tutte le eccezioni con lo stesso codice spesso non è l'approccio migliore.
Questo è comunemente usato quando qualsiasi routine interna di gestione delle eccezioni fallisce, come ultima risorsa.

# Gestione di tipi di eccezioni specifici

try
{
    /* code to open a file */
}
catch (System.IO.FileNotFoundException)
{
    /* code to handle the file being not found */
}
catch (System.IO.UnauthorizedAccessException)
{
    /* code to handle not being allowed access to the file */
}
catch (System.IO.IOException)
{
    /* code to handle IOException or it's descendant other than the previous two */
}
catch (System.Exception)
{
    /* code to handle other errors */
}

Fare attenzione che le eccezioni vengano valutate in ordine e venga applicata l'ereditarietà. Quindi devi iniziare con quelli più specifici e terminare con il loro antenato. In un dato momento, verrà eseguito solo un blocco catch.

# Aggrega eccezioni/più eccezioni da un metodo

Chi dice che non puoi generare più eccezioni in un metodo. Se non sei abituato a giocare con AggregateExceptions potresti essere tentato di creare la tua struttura dati per rappresentare molte cose che vanno storte. C'è ovviamente un'altra struttura di dati che non è un'eccezione sarebbe più ideale come i risultati di una convalida. Anche se giochi con AggregateExceptions potresti essere dal lato ricevente e gestirle sempre senza renderti conto che possono esserti utili.

È abbastanza plausibile che un metodo venga eseguito e anche se sarà un errore nel suo insieme, vorrai evidenziare più cose che sono andate storte nelle eccezioni che vengono generate. Ad esempio, questo comportamento può essere visto con il modo in cui funzionano i metodi paralleli se un'attività era suddivisa in più thread e qualsiasi numero di essi potrebbe generare eccezioni e questo deve essere segnalato. Ecco uno stupido esempio di come potresti trarre vantaggio da questo:


   public void Run()
    {
        try
        {
            this.SillyMethod(1, 2);
        }
        catch (AggregateException ex)
        {
            Console.WriteLine(ex.Message);
            foreach (Exception innerException in ex.InnerExceptions)
            {
                Console.WriteLine(innerException.Message);
            }
        }
    }

    private void SillyMethod(int input1, int input2)
    {
        var exceptions = new List<Exception>();

        if (input1 == 1)
        {
            exceptions.Add(new ArgumentException("I do not like ones"));
        }
        if (input2 == 2)
        {
            exceptions.Add(new ArgumentException("I do not like twos"));
        }
        if (exceptions.Any())
        {
            throw new AggregateException("Funny stuff happended during execution", exceptions);
        }
    }

# Generazione di un'eccezione

Il tuo codice può, e spesso dovrebbe, generare un'eccezione quando si verifica qualcosa di insolito.

public void WalkInto(Destination destination)
{
    if (destination.Name == "Mordor")
    {
        throw new InvalidOperationException("One does not simply walk into Mordor.");
    }
    // ... Implement your normal walking code here.
}

# Eccezione thread e non gestita

AppDomain.UnhandledException Questo evento fornisce la notifica di eccezioni non rilevate. Consente all'applicazione di registrare le informazioni sull'eccezione prima che il gestore predefinito del sistema segnali l'eccezione all'utente e termini l'applicazione. Se sono disponibili informazioni sufficienti sullo stato dell'applicazione, altre azioni potrebbero essere intraprese, ad esempio il salvataggio dei dati del programma per un successivo ripristino. Si consiglia cautela, poiché i dati del programma possono danneggiarsi se le eccezioni non vengono gestite.


   /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    private static void Main(string[] args)
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledException);            
    }

Application.ThreadException Questo evento consente all'applicazione Windows Form di gestire le eccezioni altrimenti non gestite che si verificano nei thread di Windows Form. Collega i tuoi gestori di eventi all'evento ThreadException per gestire queste eccezioni, che lasceranno l'applicazione in uno stato sconosciuto. Ove possibile, le eccezioni dovrebbero essere gestite da un blocco strutturato di gestione delle eccezioni.


   /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    private static void Main(string[] args)
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledException);
        Application.ThreadException += new ThreadExceptionEventHandler(ThreadException);
    }

E infine la gestione delle eccezioni

static void UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Exception ex = (Exception)e.ExceptionObject;
        // your code
    }

static void ThreadException(object sender, ThreadExceptionEventArgs e)
    {
        Exception ex = e.Exception;
        // your code
    }

# Utilizzo dell'oggetto eccezione

Puoi creare e generare eccezioni nel tuo codice. L'istanza di un'eccezione viene eseguita allo stesso modo di qualsiasi altro oggetto C#.

Exception ex = new Exception();

// constructor with an overload that takes a message string
Exception ex = new Exception("Error message"); 

Puoi quindi utilizzare il throw parola chiave per sollevare l'eccezione:

try
{
    throw new Exception("Error");
}
catch (Exception ex)
{
    Console.Write(ex.Message); // Logs 'Error' to the output window
} 

Nota: Se stai lanciando una nuova eccezione all'interno di un catch block, assicurati che l'eccezione originale sia passata come "eccezione interna", ad es.

void DoSomething() 
{
    int b=1; int c=5;
    try
    {
        var a = 1; 
        b = a - 1;
        c = a / b;
        a = a / c;
    }        
    catch (DivideByZeroException dEx) when (b==0)
    {
        // we're throwing the same kind of exception
        throw new DivideByZeroException("Cannot divide by b because it is zero", dEx);
    }
    catch (DivideByZeroException dEx) when (c==0)
    {
        // we're throwing the same kind of exception
        throw new DivideByZeroException("Cannot divide by c because it is zero", dEx);
    }
}

void Main()
{    
    try
    {
        DoSomething();
    }
    catch (Exception ex)
    {
        // Logs full error information (incl. inner exception)
        Console.Write(ex.ToString()); 
    }    
}

In questo caso si presume che l'eccezione non possa essere gestita ma al messaggio vengono aggiunte alcune informazioni utili (e si può comunque accedere all'eccezione originale tramite ex.InnerException da un blocco di eccezioni esterno).

Mostrerà qualcosa come:

System.DivideByZeroException:impossibile dividere per b perché è zero ---> System.DivideByZeroException:tentativo di dividere per zero.
in UserQuery.g__DoSomething0_0() in C:[...]\LINQPadQuery.cs:line 36
--- Fine della traccia dello stack di eccezioni interno ---
in UserQuery.g__DoSomething0_0() in C:[...]\LINQPadQuery.cs:line 42
in UserQuery.Main() in C:[...]\LINQPadQuery.cs:line 55

Se stai provando questo esempio in LinqPad, noterai che i numeri di riga non sono molto significativi (non sempre ti aiutano). Ma passare un testo di errore utile come suggerito sopra spesso riduce significativamente il tempo per rintracciare la posizione dell'errore, che in questo esempio è chiaramente la riga

c =a / b;

nella funzione DoSomething() .

Provalo in .NET Fiddle

# Implementazione di IErrorHandler per i servizi WCF

L'implementazione di IErrorHandler per i servizi WCF è un ottimo modo per centralizzare la gestione e la registrazione degli errori. L'implementazione mostrata qui dovrebbe rilevare qualsiasi eccezione non gestita generata a seguito di una chiamata a uno dei servizi WCF. In questo esempio viene mostrato anche come restituire un oggetto personalizzato e come restituire JSON anziché l'XML predefinito.

Implementare IErrorHandler:

using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Runtime.Serialization.Json;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace BehaviorsAndInspectors
{
    public class ErrorHandler : IErrorHandler
    {

        public bool HandleError(Exception ex)
        {
            // Log exceptions here

            return true;

        } // end

        public void ProvideFault(Exception ex, MessageVersion version, ref Message fault)
        {
            // Get the outgoing response portion of the current context
            var response = WebOperationContext.Current.OutgoingResponse;

            // Set the default http status code 
            response.StatusCode = HttpStatusCode.InternalServerError;

            // Add ContentType header that specifies we are using JSON
            response.ContentType = new MediaTypeHeaderValue("application/json").ToString();

            // Create the fault message that is returned (note the ref parameter) with BaseDataResponseContract                
            fault = Message.CreateMessage(
                version,
                string.Empty,
                new CustomReturnType { ErrorMessage = "An unhandled exception occurred!" },
                new DataContractJsonSerializer(typeof(BaseDataResponseContract), new List<Type> { typeof(BaseDataResponseContract) }));

            if (ex.GetType() == typeof(VariousExceptionTypes))
            {
                 // You might want to catch different types of exceptions here and process them differently
            }

            // Tell WCF to use JSON encoding rather than default XML
            var webBodyFormatMessageProperty = new WebBodyFormatMessageProperty(WebContentFormat.Json);
            fault.Properties.Add(WebBodyFormatMessageProperty.Name, webBodyFormatMessageProperty);

        } // end

    } // end class

} // end namespace

In questo esempio colleghiamo il gestore al comportamento del servizio. Puoi anche allegare questo a IEndpointBehavior, IContractBehavior o IOperationBehavior in modo simile.

Allega ai comportamenti del servizio:

using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

namespace BehaviorsAndInspectors
{
    public class ErrorHandlerExtension : BehaviorExtensionElement, IServiceBehavior
    {
        public override Type BehaviorType
        {
            get { return GetType(); }
        }

        protected override object CreateBehavior()
        {
            return this;
        }

        private IErrorHandler GetInstance()
        {
            return new ErrorHandler();
        }

        void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } // end

        void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            var errorHandlerInstance = GetInstance();

            foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
            {
                dispatcher.ErrorHandlers.Add(errorHandlerInstance);
            }
        }

        void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } // end
      
    } // end class

} // end namespace

Configurazioni in Web.config:

...
<system.serviceModel>

    <services>      
      <service name="WebServices.MyService">
        <endpoint binding="webHttpBinding" contract="WebServices.IMyService" />
      </service>
    </services>

    <extensions>      
      <behaviorExtensions>        
        <!-- This extension if for the WCF Error Handling-->
        <add name="ErrorHandlerBehavior" type="WebServices.BehaviorsAndInspectors.ErrorHandlerExtensionBehavior, WebServices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />      
      </behaviorExtensions>    
    </extensions>

    <behaviors>          
      <serviceBehaviors>        
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <ErrorHandlerBehavior />
        </behavior>     
      </serviceBehaviors>    
    </behaviors>

    ....
</system.serviceModel>
...

Ecco alcuni link che possono essere utili su questo argomento:

https://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.ierrorhandler(v=vs.100).aspx

http://www.brainthud.com/cards/5218/25441/which-four-behavior-interfaces-exist-for-interacting-with-a-service-or-client-description-what-methods-do-they- implementare-e

Altri esempi:

IErrorHandler restituisce il corpo del messaggio errato quando il codice di stato HTTP è 401 Non autorizzato

IErrorHandler non sembra gestire i miei errori in WCF .. qualche idea?

Come fare in modo che il gestore degli errori WCF personalizzato restituisca una risposta JSON con codice http non OK?

Come si imposta l'intestazione Content-Type per una richiesta HttpClient?

# Annidamento di eccezioni e prova a catturare i blocchi.

Si è in grado di annidare un'eccezione / try catch blocco dentro l'altro.

In questo modo è possibile gestire piccoli blocchi di codice in grado di funzionare senza interrompere l'intero meccanismo.

try 
{
//some code here
    try 
    {
        //some thing which throws an exception. For Eg : divide by 0
    }
    catch (DivideByZeroException dzEx)
    {
        //handle here only this exception
        //throw from here will be passed on to the parent catch block
    }
    finally
    {
        //any thing to do after it is done.
    }
 //resume from here & proceed as normal; 
}
catch(Exception e)
{
    //handle here
}

Nota: Evita le eccezioni di ingestione quando lanci al blocco di cattura genitore