Invio e ricezione degli errori

Gli errori SOAP trasportano informazioni sulla condizione di errore da un servizio a un client e, nel caso duplex, da un client a un servizio in modo interoperativo. In genere, un servizio definisce un contenuto di errore personalizzato e specifica quali operazioni possono restituirlo. (Per altre informazioni, vedere Definizione e specifica degli errori.) In questo argomento vengono descritti il modo in cui un servizio o un client duplex può inviare tali errori quando si verifica la condizione di errore corrispondente e la modalità con cui un'applicazione client o server gestisce tali errori. Per una panoramica della gestione degli errori nelle applicazioni Windows Communication Foundation (WCF), vedere Specifica e gestione di errori in contratti e servizi.

Invio di errori SOAP

Gli errori SOAP dichiarati sono quelli in cui un'operazione presenta un System.ServiceModel.FaultContractAttribute che specifica un tipo di errore SOAP personalizzato. Gli errori SOAP non dichiarati sono quelli che non vengono specificati nel contratto per un'operazione.

Invio di errori dichiarati

Per inviare un errore SOAP dichiarato, rilevare la condizione di errore adatta all'errore SOAP e generare una nuova eccezione System.ServiceModel.FaultException<TDetail> in cui il parametro di tipo è un nuovo oggetto del tipo specificato nell'attributo FaultContractAttribute di tale operazione. Nell'esempio di codice seguente viene illustrato come utilizzare l'attributo FaultContractAttribute per specificare che l'operazione SampleMethod può restituire un errore SOAP contente informazioni dettagliate di tipo GreetingFault.

[OperationContract]
[FaultContractAttribute(
  typeof(GreetingFault),
  Action="http://www.contoso.com/GreetingFault",
  ProtectionLevel=ProtectionLevel.EncryptAndSign
  )]
string SampleMethod(string msg);
<OperationContract, FaultContractAttribute(GetType(GreetingFault), Action:="http://www.contoso.com/GreetingFault", ProtectionLevel:=ProtectionLevel.EncryptAndSign)> _
Function SampleMethod(ByVal msg As String) As String

Per trasportare le informazioni sull'errore GreetingFault al client, intercettare la condizione di errore appropriata e generare una nuova eccezione System.ServiceModel.FaultException<TDetail> di tipo GreetingFault con un nuovo oggetto GreetingFault come argomento, come mostrato nell'esempio di codice seguente. Se il client è un'applicazione client WCF, viene considerata come un'eccezione gestita System.ServiceModel.FaultException<TDetail> di tipo GreetingFault.

throw new FaultException<GreetingFault>(new GreetingFault("A Greeting error occurred. You said: " + msg));
    Throw New FaultException(Of GreetingFault)(New GreetingFault("A Greeting error occurred. You said: " & msg))
End If

Invio di errori non dichiarati

L'invio di errori non dichiarati può essere molto utile per eseguire rapidamente l'analisi diagnostica e il debug dei problemi che si verificano nelle applicazioni WCF. Tuttavia, l'utilità di questo tipo di errori come strumento di debug è limitata. Più in generale, quando si esegue il debug è consigliabile utilizzare la proprietà ServiceDebugBehavior.IncludeExceptionDetailInFaults. Quando si imposta questo valore su True, i client considerano questi errori come eccezioni FaultException<TDetail> di tipo ExceptionDetail.

Importante

Poiché le eccezioni gestite possono esporre informazioni interne dell'applicazione, se si imposta la proprietà ServiceBehaviorAttribute.IncludeExceptionDetailInFaults o la proprietà ServiceDebugBehavior.IncludeExceptionDetailInFaults su true, i client WCF possono ottenere informazioni sulle eccezioni delle operazioni interne del servizio, che possono contenere informazioni personali o comunque riservate.

Di conseguenza, l'impostazione della proprietà ServiceBehaviorAttribute.IncludeExceptionDetailInFaults o della proprietà ServiceDebugBehavior.IncludeExceptionDetailInFaults su true è consigliabile solo come modalità temporanea di debug di un'applicazione di servizio. Inoltre, il codice WSDL di un metodo che restituisce in questo modo eccezioni gestite senza tuttavia gestirle non contiene il contratto dell'eccezione FaultException<TDetail> di tipo ExceptionDetail. Per ottenere correttamente le informazioni di debug, i client devono prevedere la possibilità di ricevere un errore SOAP sconosciuto, restituito ai client WCF come oggetti System.ServiceModel.FaultException.

Per inviare un errore SOAP non dichiarato, generare un oggetto System.ServiceModel.FaultException (ovvero, non l'eccezione generica FaultException<TDetail>) e passare la stringa al costruttore. Questa viene esposta alle applicazioni client WCF come un'eccezione System.ServiceModel.FaultException generata in cui la stringa è disponibile chiamando il metodo FaultException<TDetail>.ToString.

Nota

Se si dichiara un errore SOAP di tipo stringa e quindi si genera nel servizio l'eccezione corrispondente a tale errore come eccezione FaultException<TDetail> in cui il parametro di tipo è una stringa System.String, il valore di tale stringa viene assegnato alla proprietà FaultException<TDetail>.Detail e non è ottenibile tramite il metodo FaultException<TDetail>.ToString.

Gestione degli errori

Nei client WCF, gli errori SOAP che si verificano durante le comunicazioni attinenti alle applicazioni client vengono generati come eccezioni gestite. Benché esistano molte eccezioni che possono verificarsi durante l'esecuzione di un programma, le applicazioni che utilizzano il modello di programmazione dei client WCF possono prevedere di gestire i due tipi di eccezione seguenti in caso di errore di comunicazione.

Gli oggetti TimeoutException vengono generati quando un'operazione supera il periodo di timeout specificato.

Gli oggetti CommunicationException vengono generati quando nel servizio o nel client si verifica una condizione di errore di comunicazione risolvibile.

La classe CommunicationException dispone di due tipi derivati importanti: FaultException e il tipo FaultException<TDetail> generico.

Le eccezioni FaultException vengono generate quando un listener riceve un errore che non è previsto o non è specificato nel contratto dell'operazione. Ciò in genere si verifica quando durante il debug dell'applicazione la proprietà ServiceDebugBehavior.IncludeExceptionDetailInFaults del servizio è impostata su true.

Le eccezioni FaultException<TDetail> vengono generate nel client quando un errore specificato nel contratto dell'operazione viene ricevuto in risposta a un'operazione bidirezionale, ovvero a un metodo avente un attributo OperationContractAttribute in cui la proprietà IsOneWay è impostata su false.

Nota

Quando la proprietà ServiceBehaviorAttribute.IncludeExceptionDetailInFaults o la proprietà ServiceDebugBehavior.IncludeExceptionDetailInFaults di un servizio WCF è impostata su true, il client considera questa condizione come un'eccezione FaultException<TDetail> non dichiarata di tipo ExceptionDetail. I client possono intercettare questo errore specifico oppure gestire l'errore in un blocco catch per l'eccezione FaultException.

In genere, solo le eccezioni FaultException<TDetail>, TimeoutException e CommunicationException sono di interesse per client e servizi.

Nota

Naturalmente, si verificano anche eccezioni di altro tipo. Un esempio di eccezione imprevista è l'errore irreparabile System.OutOfMemoryException. In genere, le applicazioni non devono intercettare tali metodi.

Intercettazione delle eccezioni di errore nell'ordine corretto

Poiché la classe FaultException<TDetail> deriva dalla classe FaultException e la classe FaultException deriva dalla classe CommunicationException è importante intercettare queste eccezioni nell'ordine corretto. Se ad esempio si utilizza un blocco try/catch nel quale si intercetta prima l'eccezione CommunicationException, tutti gli errori SOAP, siano essi specificati o non specificati, vengono gestiti in tale blocco. Qualsiasi blocco catch successivo finalizzato alla gestione di eccezioni FaultException<TDetail> personalizzate non viene mai chiamato.

Si tenga presente che una stessa operazione può restituire un numero qualsiasi di errori specificati. Ogni errore è un tipo univoco e deve essere gestito in modo individuale.

Gestione delle eccezioni in caso di chiusura del canale

La maggior parte di quanto già descritto in questo argomento riguarda gli errori inviati durante l'elaborazione dei messaggi dell'applicazione, ovvero dei messaggi inviati esplicitamente dal client quando l'applicazione client chiama determinate operazioni nell'oggetto client WCF.

Anche nel caso di oggetti locali, l'eliminazione dell'oggetto può generare oppure nascondere le eccezioni che si verificano durante il processo di riciclo. Un comportamento simile può verificarsi quando si utilizzano gli oggetti client WCF. La chiamata di un'operazione comporta l'invio di messaggi tramite una connessione stabilita. Se la connessione è già stata chiusa oppure non può essere chiusa in modo corretto, la chiusura del canale può generare eccezioni. Ciò si verifica anche se tutte le operazioni hanno eseguito la restituzione in modo corretto.

In genere i canali dell'oggetto client vengono chiusi in uno dei casi seguenti:

  • Quando l'oggetto client WCF viene riciclato.

  • Quando l'applicazione client chiama il metodo ClientBase<TChannel>.Close.

  • Quando l'applicazione client chiama il metodo ICommunicationObject.Close.

  • Quando l'applicazione client chiama un'operazione di chiusura di una sessione.

In tutti i casi, quando viene chiuso, il canale inizia a chiudere qualsiasi canale sottostante in grado di inviare messaggi allo scopo di supportare funzionalità complesse al livello applicazione. Ad esempio, quando un contratto richiede le sessioni, un'associazione tenta di stabilire una sessione scambiando messaggi con il canale del servizio fino a stabilire una sessione. Quando il canale viene chiuso, il canale della sessione sottostante comunica al servizio che la sessione è terminata. In questo caso, se il canale risulta interrotto, chiuso o comunque inutilizzabile (ad esempio quando si scollega un cavo di rete), il canale client non può comunicare al canale del servizio che la sessione è terminata e pertanto è possibile che venga generata un'eccezione.

Interruzione del canale

Poiché anche la chiusura del canale può generare eccezioni, oltre a intercettare le eccezioni nell'ordine corretto è consigliabile interrompere il canale utilizzato per effettuare la chiamata al blocco catch.

Se l'errore trasporta informazioni sull'errore specifiche di un'operazione ed esiste la possibilità che tali informazioni possano essere utilizzate da terzi, non occorre interrompere il canale. Si tratta tuttavia di un caso raro. In tutti gli altri casi è consigliabile interrompere il canale. Per un esempio relativo a tutti questi punti, vedere Eccezioni previste.

Nell'esempio di codice seguente viene mostrato come gestire le eccezioni di errore SOAP in un'applicazione client di base, sia nel caso di errore dichiarato sia nel caso di errore non dichiarato.

Nota

Questo esempio di codice non utilizza il costrutto using. Poiché la chiusura di canali può generare eccezioni, è consigliabile fare in modo che le applicazioni prevedano anzitutto la creazione di un client WCF e quindi l'apertura, l'utilizzo e la chiusura di tale client WCF nello stesso blocco try. Per informazioni dettagliate, vedere Panoramica dei client WCF e Usare Chiudi e Interrompi per rilasciare risorse client WCF.

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Microsoft.WCF.Documentation;

public class Client
{
  public static void Main()
  {
    // Picks up configuration from the config file.
    SampleServiceClient wcfClient = new SampleServiceClient();
    try
    {
      // Making calls.
      Console.WriteLine("Enter the greeting to send: ");
      string greeting = Console.ReadLine();
      Console.WriteLine("The service responded: " + wcfClient.SampleMethod(greeting));

      Console.WriteLine("Press ENTER to exit:");
      Console.ReadLine();

      // Done with service.
      wcfClient.Close();
      Console.WriteLine("Done!");
    }
    catch (TimeoutException timeProblem)
    {
      Console.WriteLine("The service operation timed out. " + timeProblem.Message);
      Console.ReadLine();
      wcfClient.Abort();
    }
    catch (FaultException<GreetingFault> greetingFault)
    {
      Console.WriteLine(greetingFault.Detail.Message);
      Console.ReadLine();
      wcfClient.Abort();
    }
    catch (FaultException unknownFault)
    {
      Console.WriteLine("An unknown exception was received. " + unknownFault.Message);
      Console.ReadLine();
      wcfClient.Abort();
    }
    catch (CommunicationException commProblem)
    {
      Console.WriteLine("There was a communication problem. " + commProblem.Message + commProblem.StackTrace);
      Console.ReadLine();
      wcfClient.Abort();
    }
  }
}

Imports System.ServiceModel
Imports System.ServiceModel.Channels
Imports Microsoft.WCF.Documentation

Public Class Client
    Public Shared Sub Main()
        ' Picks up configuration from the config file.
        Dim wcfClient As New SampleServiceClient()
        Try
            ' Making calls.
            Console.WriteLine("Enter the greeting to send: ")
            Dim greeting As String = Console.ReadLine()
            Console.WriteLine("The service responded: " & wcfClient.SampleMethod(greeting))

            Console.WriteLine("Press ENTER to exit:")
            Console.ReadLine()

            ' Done with service. 
            wcfClient.Close()
            Console.WriteLine("Done!")
        Catch timeProblem As TimeoutException
            Console.WriteLine("The service operation timed out. " & timeProblem.Message)
            Console.ReadLine()
            wcfClient.Abort()
        Catch greetingFault As FaultException(Of GreetingFault)
            Console.WriteLine(greetingFault.Detail.Message)
            Console.ReadLine()
            wcfClient.Abort()
        Catch unknownFault As FaultException
            Console.WriteLine("An unknown exception was received. " & unknownFault.Message)
            Console.ReadLine()
            wcfClient.Abort()
        Catch commProblem As CommunicationException
            Console.WriteLine("There was a communication problem. " & commProblem.Message + commProblem.StackTrace)
            Console.ReadLine()
            wcfClient.Abort()
        End Try
    End Sub
End Class

Vedi anche