Je tente d’étendre cette réponse sur SO pour qu’un client WCF réessaie sur des défaillances de réseau transitoires et gère d’ autres situations qui nécessitent une nouvelle tentative, telle que l’expiration de l’authentification.
Question:
Quelles sont les exceptions WCF à gérer et quelle est la manière correcte de les gérer?
Voici quelques exemples de techniques que j’espère voir au lieu de ou en plus de proxy.abort()
:
Comme 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.Use(orderService=> //{ // newOrderId = orderService.PlaceOrder(request); //} /// /// A safe WCF Proxy suitable when sessionmode=false /// /// public static void Use(UseServiceDelegateVoid 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(); } } }
EDIT: Il semble y avoir des inefficacités à fermer et à rouvrir le client plusieurs fois. J’explore les solutions ici et mettrai à jour et développerai ce code s’il y en a un. (ou si David Khaykin affiche 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 cet article sur le blog de la machine de retour ) pour gérer les tentatives de traitement WCF et gérer les exceptions.
J’ai étudié chaque exception, ce que je voudrais faire avec cette exception et 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 mettant le client dans un état invalide provenait également d’une classe de base partagée.
L’exemple suivant intercepte chaque exception WCF par laquelle un client peut passer et est extensible pour vos propres erreurs de canal personnalisées.
Exemple d’utilisation du client WCF
Une fois que vous générez votre proxy côté client, c’est tout ce dont vous avez besoin pour l’implémenter.
Service.Use(orderService=> { orderService.PlaceOrder(request); }
ServiceDelegate.cs
Ajoutez ce fichier à votre solution. Aucune modification n’est nécessaire pour ce fichier, sauf si vous souhaitez modifier le nombre de tentatives ou les exceptions que vous souhaitez gérer.
public delegate void UseServiceDelegate(T proxy); public static class Service { public static ChannelFactory _channelFactory = new ChannelFactory (""); public static void Use(UseServiceDelegate 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 ); } } }
J’ai commencé un projet sur Codeplex qui présente les fonctionnalités suivantes
http://smartwcfclient.codeplex.com/
C’est un travail en cours et 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(channelFactory); reusableSW.Reuse(client => { client.CheckIn(count.ToSsortingng()); }); reusableSW.Dispose();
Nous avons un client WCF qui gère presque tous les types de pannes sur le serveur. La liste de capture est très longue mais ne doit pas nécessairement l’être. Si vous regardez de près, vous verrez que de nombreuses exceptions sont des définitions enfant de la classe d’exception (et de quelques autres classes).
Ainsi, vous pouvez simplifier les choses si vous le souhaitez. Cela dit, voici quelques erreurs typiques que nous relevons:
Délai d’attente du serveur
Serveur trop occupé
Serveur indisponible.
Les liens ci-dessous peuvent aider à gérer les exceptions WCF: