2010-01-05 6 views
11

Lorsqu'une seule ClientBase<T> instance est utilisé pour plusieurs appels de service WCF, il peut obtenir un canal dans un état de défaut (ie. Lorsque le service est en panne).Comment soigner les canaux WCF défaillants?

Je voudrais guérir le canal automatiquement lorsque le service arrive à nouveau. La seule façon que j'ai trouvé est d'appeler le code suivant avant chaque appel de méthode:

if (clientBase.InnerChannel.State == CommunicationState.Faulted) 
{ 
     clientBase.Abort(); 
     ((IDisposable)clientBase).Dispose(); 
     clientBase = new SampleServiceClientBase(); 
} 

j'ai eu le sentiment que ce n'est pas la bonne façon de le faire. Quelqu'un a une meilleure idée?

Répondre

18

Vous ne pouvez pas. Une fois qu'un canal est en panne, il est définitivement bloqué. Vous devez créer un nouveau canal. Les canaux WCF sont à état (en quelque sorte parlant), donc un canal défectueux signifie que l'état peut être corrompu.

Ce que vous pouvez faire est de mettre la logique que vous utilisez dans une méthode utilitaire:

public static class Service<T> where T : class, ICommunicationObject, new() 
{ 
    public static void AutoRepair(ref T co) 
    { 
     AutoRepair(ref co,() => new T()); 
    } 

    public static void AutoRepair(ref T co, Func<T> createMethod) 
    { 
     if ((co != null) && (co.State == CommunicationState.Faulted)) 
     { 
      co.Abort(); 
      co = null; 
     } 
     if (co == null) 
     { 
      co = createMethod(); 
     } 
    } 
} 

Ensuite, vous pouvez appeler votre service avec ce qui suit:

Service<SampleServiceClient>.AutoRepair(ref service, 
    () => new SampleServiceClient(someParameter)); 
service.SomeMethod(); 

Ou si vous voulez utiliser le constructeur par défaut sans défaut, juste:

Service<SampleServiceClient>.AutoRepair(ref service); 
service.SomeMethod(); 

Comme il gère également le cas où t Le service est null, vous n'avez pas besoin d'initialiser le service avant de l'appeler.

À peu près tout ce que je peux offrir de mieux. Peut-être que quelqu'un d'autre a un meilleur moyen.

+1

Avez-vous besoin de T également pour implémenter IDisposable? –

+0

@DavidGardiner: Pas si elle implémente 'ICommunicationObject'. L'implémentation 'Dispose' sur les canaux WCF fait partie du problème. – Aaronaught

+0

Mais l'interface ICommunicationObject (http://msdn.microsoft.com/en-us/library/system.servicemodel.icommunicationobject.aspx) ne met pas en œuvre IDisposable - donc l'exemple ci-dessus ne fonctionnera pas sans jeter à co IDisposable. –

0

C'est ce que je suis en train de faire, mais je ne peux pas dire que c'est la meilleure option soit.

Je recréent le proxy lorsqu'une exception est prise sur l'appel.

try 
{ 
    ListCurrentProcesses(); 
} 
catch (TypeLoadException ex) 
{ 
    Debug.Print("Oops: " + ex.Message); 
    m_Proxy = new ProcessManagerProxy(); 
} 
catch (EndpointNotFoundException endpointEX) 
{ 
    Debug.Print("Oops: " + endpointEX.Message); 
    m_Proxy = new ProcessManagerProxy(); 
} 
catch (CommunicationException communicationEx) 
{ 
    Debug.Print("Oops: " + communicationEx.Message); 
    m_Proxy = new ProcessManagerProxy(); 
} 
+0

Ne jamais attraper 'SystemException', ** en particulier ** si vous ne relancez pas. Cet arbre inclut des instances comme 'OutOfMemoryException' et' StackOverflowException'. De plus, vous ne disposez pas correctement de l'ancienne chaîne ici. – Aaronaught

+0

Compris. Ce n'était qu'un exemple rapide. Voyez-vous un gros problème en laissant le canal être collecté au lieu de l'éliminer explicitement? Je suppose que nous n'allons pas répéter cette opération 1000 fois avant d'abandonner. –

+0

Je l'ai essayé et cela a causé plus de problèmes –

Questions connexes