web-dev-qa-db-fra.com

Comment gérer les exceptions WCF (liste consolidée avec code)

J'essaie d'étendre cette réponse sur SO pour faire réessayer un client WCF sur les pannes de réseau transitoires et gérer d'autres situations qui nécessitent une nouvelle tentative telles que l'expiration de l'authentification.

Question:

Quelles sont les exceptions WCF qui doivent être gérées et quelle est la bonne façon de les gérer?

Voici quelques exemples de techniques que j'espère voir à la place ou en plus de proxy.abort():

  • Délai X secondes avant de réessayer
  • Fermez et recréez un client WCF New (). Jetez l'ancien.
  • Ne réessayez pas et ne renvoyez pas cette erreur
  • Réessayez N fois, puis lancez

Puisqu'il est peu probable qu'une personne connaisse toutes les exceptions ou les moyens de les résoudre, partagez ce que vous savez. Je vais agréger les réponses et les approches dans l'exemple de code ci-dessous.

    // USAGE SAMPLE
    //int newOrderId = 0; // need a value for definite assignment
    //Service<IOrderService>.Use(orderService=>
    //{
    //  newOrderId = orderService.PlaceOrder(request);
    //}




    /// <summary>
    /// A safe WCF Proxy suitable when sessionmode=false
    /// </summary>
    /// <param name="codeBlock"></param>
    public static void Use(UseServiceDelegateVoid<T> codeBlock)
    {
        IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
        bool success = false;
        try
        {
            codeBlock((T)proxy);
            proxy.Close();
            success = true;
        }
        catch (CommunicationObjectAbortedException e)
        {
                // Object should be discarded if this is reached.  
                // Debugging discovered the following exception here:
                // "Connection can not be established because it has been aborted" 
            throw e;
        }
        catch (CommunicationObjectFaultedException e)
        {
            throw e;
        }
        catch (MessageSecurityException e)
        {
            throw e;
        }
        catch (ChannelTerminatedException)
        {
            proxy.Abort(); // Possibly retry?
        }
        catch (ServerTooBusyException)
        {
            proxy.Abort(); // Possibly retry?
        }
        catch (EndpointNotFoundException)
        {
            proxy.Abort(); // Possibly retry?
        }
        catch (FaultException)
        {
            proxy.Abort();
        }
        catch (CommunicationException)
        {
            proxy.Abort();
        }
        catch (TimeoutException)
        {
         // Sample error found during debug: 

         // The message could not be transferred within the allotted timeout of 
         //  00:01:00. There was no space available in the reliable channel's 
         //  transfer window. The time allotted to this operation may have been a 
         //  portion of a longer timeout.

            proxy.Abort();
        }
        catch (ObjectDisposedException )
        {
            //todo:  handle this duplex callback exception.  Occurs when client disappears.  
            // Source: https://stackoverflow.com/questions/1427926/detecting-client-death-in-wcf-duplex-contracts/1428238#1428238
        }
        finally
        {
            if (!success)
            {
                proxy.Abort();
            }
        }
    }
48

EDIT: Il semble y avoir des inefficacités avec la fermeture et la réouverture du client plusieurs fois. Je suis en explorant les solutions ici et je mettrai à jour et développerai ce code s'il en trouve un. (ou si David Khaykin publie une réponse, je la marquerai comme acceptée)

Après avoir bricolé avec cela pendant quelques années, le code ci-dessous est ma stratégie préférée ( après avoir vu ce blog sur la machine de retour ) pour gérer les tentatives WCF et gérer les exceptions.

J'ai étudié chaque exception, ce que je voudrais faire avec cette exception et j'ai remarqué un trait commun; chaque exception nécessitant une "nouvelle tentative" héritée d'une classe de base commune. J'ai également remarqué que chaque exception permFail qui mettait le client dans un état non valide provenait également d'une classe de base partagée.

L'exemple suivant intercepte toutes les exceptions WCF qu'un client pourrait traverser et est extensible pour vos propres erreurs de canal personnalisé.

Exemple d'utilisation du client WCF

Une fois que vous avez généré votre proxy côté client, c'est tout ce dont vous avez besoin pour l'implémenter.

Service<IOrderService>.Use(orderService=>
{
  orderService.PlaceOrder(request);
}

ServiceDelegate.cs

Ajoutez ce fichier à votre solution. Aucune modification n'est nécessaire dans ce fichier, sauf si vous souhaitez modifier le nombre de tentatives ou les exceptions que vous souhaitez gérer.

public delegate void UseServiceDelegate<T>(T proxy);

public static class Service<T>
{
    public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>(""); 

    public static void Use(UseServiceDelegate<T> codeBlock)
    {
        IClientChannel proxy = null;
        bool success = false;


       Exception mostRecentEx = null;
       int millsecondsToSleep = 1000;

       for(int i=0; i<5; i++)  // Attempt a maximum of 5 times 
       {
           // Proxy cann't be reused
           proxy = (IClientChannel)_channelFactory.CreateChannel();

           try
           {
               codeBlock((T)proxy);
               proxy.Close();
               success = true; 
               break;
           }
           catch (FaultException customFaultEx)
           {
               mostRecentEx = customFaultEx;
               proxy.Abort();

               //  Custom resolution for this app-level exception
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           }

           // The following is typically thrown on the client when a channel is terminated due to the server closing the connection.
           catch (ChannelTerminatedException cte)
           {
              mostRecentEx = cte;
               proxy.Abort();
               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep  * (i + 1)); 
           }

           // The following is thrown when a remote endpoint could not be found or reached.  The endpoint may not be found or 
           // reachable because the remote endpoint is down, the remote endpoint is unreachable, or because the remote network is unreachable.
           catch (EndpointNotFoundException enfe)
           {
              mostRecentEx = enfe;
               proxy.Abort();
               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           }

           // The following exception that is thrown when a server is too busy to accept a message.
           catch (ServerTooBusyException stbe)
           {
              mostRecentEx = stbe;
               proxy.Abort();

               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           }
           catch (TimeoutException timeoutEx)
           {
               mostRecentEx = timeoutEx;
               proxy.Abort();

               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           } 
           catch (CommunicationException comException)
           {
               mostRecentEx = comException;
               proxy.Abort();

               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           }


           catch(Exception e)
           {
                // rethrow any other exception not defined here
                // You may want to define a custom Exception class to pass information such as failure count, and failure type
                proxy.Abort();
                throw e;  
           }
       }
       if (success == false && mostRecentEx != null) 
       { 
           proxy.Abort();
           throw new Exception("WCF call failed after 5 retries.", mostRecentEx );
       }

    }
}
21
goodguys_activate

J'ai commencé un projet sur Codeplex qui a les fonctionnalités suivantes

  • Permet une réutilisation efficace du proxy client
  • Nettoie toutes les ressources, y compris les gestionnaires d'événements
  • Fonctionne sur les canaux duplex
  • Fonctionne sur les services par appel
  • Prend en charge le constructeur de config, ou par usine

http://smartwcfclient.codeplex.com/

C'est un travail en cours, et est très fortement commenté. J'apprécierai tout commentaire concernant son amélioration.

Exemple d'utilisation en mode instance:

 var reusableSW = new LC.Utils.WCF.ServiceWrapper<IProcessDataDuplex>(channelFactory);

 reusableSW.Reuse(client =>
                      {
                          client.CheckIn(count.ToString());
                      });


 reusableSW.Dispose();
4
2
Pradeep

nous avons un client WCF qui gère presque tous les types de pannes sur le serveur. La liste des captures est très longue mais ne doit pas l'être. Si vous regardez attentivement, vous verrez que de nombreuses exceptions sont des définitions enfants de la classe d'exception (et quelques autres classes).

Ainsi, vous pouvez beaucoup simplifier les choses si vous le souhaitez. Cela dit, voici quelques erreurs typiques que nous détectons:

Délai d'expiration du serveur
Serveur trop occupé
Serveur indisponible.

2
Frode Stenstrøm