425 votes

Quelle est la meilleure solution pour le client WCF « using » bloc question ?

J'aime l'instanciation de mon service WCF clients au sein d'un using bloc comme il est à peu près la norme de façon à utiliser les ressources qui implémentent IDisposable:

using (var client = new SomeWCFServiceClient()) 
{
    //Do something with the client 
}

Mais, comme indiqué dans cet article MSDN, l'enveloppant d'un WCF client dans un using bloc pourrait masquer les erreurs qui en résultent dans le client se retrouve dans une reproché à l'etat (comme un délai d'attente ou de problème de communication). Longue histoire courte, lorsque dispose() est appelée, la méthode Close() les feux, mais renvoie une erreur car il est dans un état faulted. L'exception d'origine est alors masqué par la deuxième exception. Pas bonne.

La solution de contournement proposée dans l'article MSDN est d'éviter complètement à l'aide d'un using bloc, et au lieu d'instancier vos clients et de les utiliser quelque chose comme ceci:

try
{
    ...
    client.Close();
}
catch (CommunicationException e)
{
    ...
    client.Abort();
}
catch (TimeoutException e)
{
    ...
    client.Abort();
}
catch (Exception e)
{
    ...
    client.Abort();
    throw;
}

Par rapport à l' using bloc, je pense que c'est laid. Et beaucoup de code à écrire à chaque fois vous avez besoin d'un client.

Heureusement, j'ai trouvé quelques autres solutions, comme celle-ci sur IServiceOriented. Vous commencez avec:

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 = (IClientChannel)_channelFactory.CreateChannel(); 
        bool success = false; 
        try 
        { 
            codeBlock((T)proxy); 
            proxy.Close(); success = true; 
        } 
        finally 
        { 
            if (!success) 
            { 
                proxy.Abort(); 
            } 
        } 
     } 
} 

Ce qui permet alors:

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

Ce n'est pas mauvais, mais je ne pense pas que c'est expressive et facilement compréhensible que l' using bloc.

La solution de contournement, je suis en train d'essayer de les utiliser j'ai lu à ce sujet sur blog.davidbarret.net. Fondamentalement, vous remplacez le client Dispose() méthode partout où vous les utilisez. Quelque chose comme:

public partial class SomeWCFServiceClient : IDisposable
{
    void IDisposable.Dispose() 
    {
        if (this.State == CommunicationState.Faulted) 
        {
            this.Abort();
        } 
        else 
        {
            this.Close();
        }
    }
}

Cela semble être en mesure de permettre à l' using bloc de nouveau, sans le danger de masquage d'une reproché à l'état d'exception.

Donc, y at-il d'autres astuces que j'ai à regarder dehors pour l'utilisation de ces solutions de contournement? A quelqu'un de venir avec quelque chose de mieux?

142voto

Marc Gravell Points 482669

En fait, bien que j'ai blogué (voir la réponse de Luc), je pense que cela (voir lien alternatif) est meilleur que mon IDisposable wrapper. Typique de code:

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

(edit par commentaires)

Depuis Use revient vide, le moyen le plus facile pour gérer les valeurs de retour est via une capture de variable:

int newOrderId = 0; // need a value for definite assignment
Service<IOrderService>.Use(orderService=>
  {
    newOrderId = orderService.PlaceOrder(request);
  });
Console.WriteLine(newOrderId); // should be updated

92voto

Matt Davis Points 22019

Étant donné le choix entre la solution préconisée par IServiceOriented.com et la solution préconisée par David Barret blog, je préfère la simplicité offerte par des raisons impérieuses le client Dispose() de la méthode. Cela me permet de continuer à utiliser l'aide de() que l'on pourrait s'attendre à un objet jetable. Cependant, comme @Brian l'a souligné, cette solution contient une condition de concurrence en ce que l'État pourrait ne pas être prise en défaut lorsqu'il est vérifié, mais pourrait être par le temps de Fermer() est appelée, dans ce cas la CommunicationException se produit encore.

Donc, pour contourner ce problème, j'ai utilisé une solution qui allie le meilleur des deux mondes.

void IDisposable.Dispose()
{
    bool success = false;
    try {
        if (State != CommunicationState.Faulted) {
            Close();
            success = true;
        }
    } finally {
        if (!success) {
            Abort();
        }
    }
}

34voto

MichaelGG Points 8202

J'ai écrit un ordre supérieur de la fonction pour le faire marcher droit. Nous avons utilisé dans plusieurs projets et il semble fonctionner à merveille. C'est la façon dont les choses ont été faites depuis le début, sans "l'aide" paradigme ou ainsi de suite.

Vous pouvez faire des appels comme ceci:

int a = 1; 
int b = 2; 
int sum = UseService((ICalculator calc) => calc.Add(a, b)); 
Console.WriteLine(sum);

C'est à peu près juste comme vous avez dans votre exemple. Dans certains projets, nous écrivons fortement typé méthodes d'assistance, on se retrouve donc écrit des choses comme "Wcf.UseFooService(f=>f...)".

Je le trouve assez élégant, toutes choses considérées. Est-il un problème particulier vous rencontrés?

Edit: je tiens à ajouter, cela permet à d'autres fonctionnalités intéressantes à être branché. Par exemple, sur un site, le site s'authentifie sur le service au nom de l'utilisateur connecté. (Le site n'a pas d'informations d'identification par lui-même.) Par l'écriture de notre propre "UseService" méthode helper, nous pouvons configurer le canal de l'usine de la façon dont nous voulons, etc. Nous sommes également pas lié à l'utilisation de l'généré procurations -- interface de le faire.

30voto

makerofthings7 Points 10028

Microsoft, qui est le moyen recommandé pour traiter WCF client appelle:

Pour plus de détail, voir: Prévu des Exceptions

try
{
    ...
    double result = client.Add(value1, value2);
    ...
    client.Close();
}
catch (TimeoutException exception)
{
    Console.WriteLine("Got {0}", exception.GetType());
    client.Abort();
}
catch (CommunicationException exception)
{
    Console.WriteLine("Got {0}", exception.GetType());
    client.Abort();
}

Des informations supplémentaires Donc, beaucoup de gens semblent poser cette question sur WCF que Microsoft a même créé un dédié exemple pour montrer comment gérer les exceptions:

c:\WF_WCF_Samples\WCF\Basic\Client\ExpectedExceptions\CS\client

Télécharger l'exemple de: C# ou VB

Considérant qu'il ya tellement de nombreuses questions impliquant l'utilisation de déclaration, (chauffée?) Des discussions à l'interne et fils sur cette question, je ne vais pas perdre mon temps à essayer de devenir un code de cow-boy et de trouver une manière plus propre. Je vais le sucer, et de mettre en œuvre WCF clients cette verbose (encore confiance) de manière, pour mon serveur d'applications.

En option Échecs Supplémentaires pour attraper

De nombreuses exceptions dérivent CommunicationException et je ne pense pas que la plupart de ces exceptions doivent être rejugés. Je drudged par le biais de chacun d'exception sur MSDN et trouvé une courte liste de retry-mesure des exceptions (en plus de TimeOutException ci-dessus). Ne laissez-moi savoir si j'ai raté une exception qui doit être retentée.

  // The following is typically thrown on the client when a channel is terminated due to the server closing the connection.
catch (ChannelTerminatedException cte)
{
secureSecretService.Abort();
// todo: Implement delay (backoff) and retry
}

// 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)
{
secureSecretService.Abort();
// todo: Implement delay (backoff) and retry
}

// The following exception that is thrown when a server is too busy to accept a message.
catch (ServerTooBusyException stbe)
{
secureSecretService.Abort();
// todo: Implement delay (backoff) and retry
}

Certes, c'est un peu banal de code à écrire. Actuellement, je préfère cette réponse, et ne pas voir de "hacks" dans ce code qui peuvent provoquer des problèmes sur la route.

14voto

Neil Points 1450

j'ai enfin trouvé quelques étapes solides vers une solution propre à ce problème.

Cette boîte à outils s'étend WCFProxyGenerator pour fournir une gestion des exceptions de proxy. Il génère un proxy supplémentaires appelés ExceptionHandlingProxy<T> qui hérite ExceptionHandlingProxyBase<T> - ce qui implémente la viande de la procuration de la fonctionnalité. Le résultat est que vous pouvez choisir d'utiliser le proxy par défaut qui hérite ClientBase<T> ou ExceptionHandlingProxy<T> qui encapsule la gestion de la durée de vie de la chaîne de l'usine et le canal. ExceptionHandlingProxy respecte vos sélections dans la boîte de dialogue Ajouter une Référence de Service à l'égard des méthodes asynchrones et les types de collection.

Codeplex a lancé un projet appelé la gestion des exceptions WCF Générateur de Proxy, il installe un nouvel outil personnalisé pour visual studio 2008, puis utiliser cet outil pour générer le nouveau service de proxy (Ajouter une référence de service), il a quelques belles fonctionnalités pour gérer les reproché canaux, les délais d'attente et l'élimination sans danger. Il y a une vidéo d'excellente qualité que l'on appelle ici ExceptionHandlingProxyWrapper expliquer exactement comment cela fonctionne.

Vous pouvez utiliser en toute sécurité l' Using déclaration à nouveau, et si le canal est reproché à toute demande (TimeoutException ou CommunicationException), le Wrapper de ré-initialiser le faillées canal et relancez la requête, si cela échoue, alors il va appeler l' Abort() de commandement et de disposer de la procuration et de renvoyer l'Exception, si le service lance un FaultException code d'arrêt de l'exécution et de la procuration sera annulée en toute sécurité en jetant l'exception correcte comme prévu.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X