J'aime instancier mes clients de service WCF dans un using
bloc car c'est à peu près la façon standard d'utiliser les ressources qui implémentent IDisposable
:
using (var client = new SomeWCFServiceClient())
{
//Do something with the client
}
Mais, comme indiqué dans cet article MSDN , encapsuler un client WCF dans un using
bloc peut masquer toutes les erreurs qui entraînent le client dans un état défectueux (comme un délai d'attente ou un problème de communication). En bref, lorsque Dispose () est appelée, la méthode Close () du client se déclenche, mais génère une erreur car elle est dans un état défectueux. L'exception d'origine est ensuite masquée par la deuxième exception. Pas bon.
La solution de contournement suggérée dans l'article MSDN consiste à éviter complètement d'utiliser un using
bloc, et à la place, instancier vos clients et 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 au using
bloc, je pense que c'est moche. Et beaucoup de code à écrire chaque fois que vous avez besoin d'un client.
Heureusement, j'ai trouvé quelques autres solutions de contournement, comme celle-ci sur IServiceOriented. Vous commencez par:
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 mal, mais je ne pense pas que ce soit aussi expressif et facilement compréhensible que le using
bloc.
La solution de contournement que j'essaie actuellement d'utiliser J'ai d'abord lu sur blog.davidbarret.net . Fondamentalement, vous remplacez la Dispose()
méthode du client partout où vous l'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 d'autoriser à using
nouveau le bloc sans risque de masquer une exception d'état défectueux.
Alors, y a-t-il d'autres problèmes que je dois rechercher pour utiliser ces solutions de contournement? Quelqu'un a-t-il trouvé quelque chose de mieux?
Action<T>
plutôtUseServiceDelegate<T>
. mineur.Service<T>
car elle complique les tests unitaires (comme la plupart des choses statiques). Je préférerais qu'il ne soit pas statique afin qu'il puisse être injecté dans la classe qui l'utilise.Réponses:
En fait, bien que je blogué (voir la réponse de Luc ), je pense que c'est mieux que mon emballage IDisposable. Code typique:
(modifier par commentaires)
Puisque
Use
return est nul, la façon la plus simple de gérer les valeurs de retour est via une variable capturée:la source
public static TResult Use<TResult>(Func<T, TResult> codeBlock) { ... }
https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
ethttps://devzone.channeladam.com/articles/2014/09/how-to-easily-call-wcf-service-properly/
ethttp://dzimchuk.net/post/wcf-error-helpers
https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
Étant donné le choix entre la solution préconisée par IServiceOriented.com et la solution préconisée par le blog de David Barret , je préfère la simplicité offerte en remplaçant la méthode Dispose () du client. Cela me permet de continuer à utiliser l'instruction using () comme on pourrait s'y attendre avec un objet jetable. Cependant, comme l'a souligné @Brian, cette solution contient une condition de concurrence critique dans la mesure où l'État peut ne pas être en défaut lors de sa vérification, mais peut l'être au moment de l'appel de Close (), auquel cas l'exception CommunicationException se produit toujours.
Donc, pour contourner cela, j'ai utilisé une solution qui mélange le meilleur des deux mondes.
la source
success
drapeau? Pourquoi ne pastry { Close(); } catch { Abort(); throw; }
?Close(); success = true;
? Je ne voudrais pas qu'une exception soit levée si je pouvais l'abandonner avec succès dans le bloc finalement. Je ne voudrais qu'une exception levée si Abort () échoue dans ce cas. De cette façon, le try / catch cacherait l'exception potentielle de condition de concurrence et vous permettrait toujours d'abandonner () la connexion dans le bloc finally.J'ai écrit une fonction d'ordre supérieur pour le faire fonctionner correctement. Nous l'avons utilisé dans plusieurs projets et cela semble très bien fonctionner. C'est ainsi que les choses auraient dû être faites dès le départ, sans le paradigme «utilisation» ou autre.
Vous pouvez effectuer des appels comme celui-ci:
C'est à peu près comme vous l'avez fait dans votre exemple. Dans certains projets, nous écrivons des méthodes d'assistance fortement typées, donc nous finissons par écrire des choses comme "Wcf.UseFooService (f => f ...)".
Je le trouve assez élégant, tout bien considéré. Y a-t-il un problème particulier que vous avez rencontré?
Cela permet de brancher d'autres fonctionnalités astucieuses. Par exemple, sur un site, le site s'authentifie auprès du service au nom de l'utilisateur connecté. (Le site n'a pas d'informations d'identification en lui-même.) En écrivant notre propre assistant de méthode "UseService", nous pouvons configurer la fabrique de canaux comme nous le voulons, etc. Nous ne sommes pas non plus tenus d'utiliser les proxys générés - n'importe quelle interface fera .
la source
GetCachedFactory
méthode?C'est la méthode recommandée par Microsoft pour gérer les appels des clients WCF:
Pour plus de détails, voir: Exceptions attendues
Informations supplémentaires Tant de gens semblent poser cette question sur WCF que Microsoft a même créé un exemple dédié pour montrer comment gérer les exceptions:
c: \ WF_WCF_Samples \ WCF \ Basic \ Client \ ExpectedExceptions \ CS \ client
Téléchargez l'exemple: C # ou VB
Étant donné qu'il y a tellement de problèmes impliquant l'instruction using , (chauffé?) Discussions internes et discussions sur cette question, je ne vais pas perdre mon temps à essayer de devenir un cow-boy de code et à trouver un moyen plus propre. Je vais simplement le sucer et implémenter les clients WCF de cette manière détaillée (mais fiable) pour mes applications serveur.
Échecs supplémentaires facultatifs pour attraper
De nombreuses exceptions dérivent de
CommunicationException
et je ne pense pas que la plupart de ces exceptions devraient être réessayées. J'ai parcouru chaque exception sur MSDN et trouvé une courte liste d'exceptions pouvant être réessayées (en plus deTimeOutException
ci-dessus). Faites-moi savoir si j'ai raté une exception qui devrait être réessayée.Certes, c'est un peu de code banal à écrire. Je préfère actuellement cette réponse , et je ne vois aucun "piratage" dans ce code qui pourrait causer des problèmes en cours de route.
la source
"Hope this code wasn't important, because it might not happen."
est toujours exécutée ...J'ai finalement trouvé quelques étapes solides vers une solution propre à ce problème.
Codeplex a un projet appelé Générateur de proxy WCF de gestion des exceptions . Il installe essentiellement un nouvel outil personnalisé dans Visual Studio 2008, puis utilise cet outil pour générer le nouveau proxy de service (Ajouter une référence de service) . Il a de belles fonctionnalités pour gérer les canaux défaillants, les délais d'attente et l'élimination en toute sécurité. Il y a une excellente vidéo ici appelée ExceptionHandlingProxyWrapper expliquant exactement comment cela fonctionne.
Vous pouvez à
Using
nouveau utiliser l' instruction en toute sécurité , et si le canal est défaillant sur toute demande (TimeoutException ou CommunicationException), le wrapper réinitialisera le canal défaillant et relancera la requête. Si cela échoue, il appellera laAbort()
commande et supprimera le proxy et renverra l'exception. Si le service envoie unFaultException
code, il cessera de s'exécuter et le proxy sera abandonné en toute sécurité en levant l'exception correcte comme prévu.la source
Sur la base des réponses de Marc Gravell, MichaelGG et Matt Davis, nos développeurs ont trouvé ce qui suit:
Exemple d'utilisation:
C'est aussi proche que possible de la syntaxe "using", vous n'avez pas à retourner une valeur fictive lors de l'appel d'une méthode void, et vous pouvez effectuer plusieurs appels au service (et renvoyer plusieurs valeurs) sans avoir à utiliser de tuples.
Vous pouvez également l'utiliser avec
ClientBase<T>
descendants au lieu de ChannelFactory si vous le souhaitez.La méthode d'extension est exposée si un développeur souhaite supprimer manuellement un proxy / canal à la place.
la source
DisposeSafely
privé est certainement une option et éviterait toute confusion. Il peut y avoir des cas d'utilisation où quelqu'un voudrait l'appeler directement, mais je ne peux pas en trouver un tout de suite.https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
@Marc Gravell
Ne serait-il pas acceptable de l'utiliser:
Ou, la même chose
(Func<T, TResult>)
en cas deService<IOrderService>.Use
Cela faciliterait le retour des variables.
la source
Qu'est-ce que c'est?
Ceci est la version CW de la réponse acceptée mais avec (ce que je considère comme complet) la gestion des exceptions incluse.
La réponse acceptée fait référence à ce site Web qui n'existe plus . Pour vous éviter des ennuis, j'inclus ici les pièces les plus pertinentes. En outre, je l'ai légèrement modifié pour inclure la gestion des tentatives d'exception pour gérer ces délais d'attente réseau ennuyeux.
Utilisation simple 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.
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.
PS: J'ai fait de ce post un wiki communautaire. Je ne collecterai pas de "points" à partir de cette réponse, mais préférez que vous le votiez si vous êtes d'accord avec la mise en œuvre, ou que vous la modifiiez pour l'améliorer.
la source
success == false
à la déclaration finale ifVous trouverez ci-dessous une version améliorée de la source de la question et étendue pour mettre en cache plusieurs usines de canaux et tenter de rechercher le point de terminaison dans le fichier de configuration par nom de contrat.
Il utilise .NET 4 (en particulier: contravariance, LINQ,
var
):la source
UseServiceDelegate<T>
au lieu deAction<T>
?Action<T>
fonctionne aussi bien.Un wrapper comme celui-ci fonctionnerait:
Cela devrait vous permettre d'écrire du code comme:
L'encapsuleur pourrait bien sûr intercepter plus d'exceptions si cela est nécessaire, mais le principe reste le même.
la source
Dispose
un IChannel, il peut lever une exception si le canal est dans un état défectueux, c'est un problème car Microsoft spécifie qu'ilDispose
ne doit jamais lancer. Donc, ce que le code ci-dessus fait est de gérer le cas quandClose
lève une exception. SiAbort
jette, cela pourrait être quelque chose de grave. J'ai écrit un article à ce sujet en décembre dernier: blog.tomasjansson.com/2010/12/disposible-wcf-client-wrapperJ'ai utilisé le proxy dynamique Castle pour résoudre le problème Dispose (), et j'ai également implémenté l'actualisation automatique du canal lorsqu'il est dans un état inutilisable. Pour l'utiliser, vous devez créer une nouvelle interface qui hérite de votre contrat de service et IDisposable. Le proxy dynamique implémente cette interface et encapsule un canal WCF:
J'aime cela, car vous pouvez injecter des services WCF sans que les consommateurs n'aient à se soucier des détails de WCF. Et il n'y a pas de cruauté supplémentaire comme les autres solutions.
Jetez un oeil au code, c'est en fait assez simple: WCF Dynamic Proxy
la source
Utilisez une méthode d'extension:
la source
Si vous n'avez pas besoin d' IoC ou utilisez un client généré automatiquement (référence de service), vous pouvez simplement utiliser un wrapper pour gérer la fermeture et laisser le GC prendre la base cliente lorsqu'il est dans un état sûr qui ne lèvera aucune exception. Le GC appellera Dispose in serviceclient, et ceci appellera
Close
. Puisqu'il est déjà fermé, il ne peut causer aucun dommage. J'utilise ceci sans problème dans le code de production.Ensuite, lorsque vous accédez au serveur, vous créez le client et utilisez
using
dans l'autodisconect:la source
Sommaire
En utilisant les techniques décrites dans cette réponse, on peut consommer un service WCF dans un bloc using avec la syntaxe suivante:
Vous pouvez bien sûr adapter cela encore plus pour obtenir un modèle de programmation plus concis spécifique à votre situation - mais le fait est que nous pouvons créer une implémentation de
IMyService
reprentation du canal qui implémente correctement le modèle jetable.Détails
Toutes les réponses données jusqu'à présent traitent du problème de contourner le «bogue» dans l'implémentation du canal WCF de
IDisposable
. La réponse qui semble offrir le modèle de programmation le plus concis (vous permettant d'utiliser leusing
bloc pour disposer de ressources non gérées) est celle-ci - où le proxy est modifié pour être implémentéIDisposable
avec une implémentation sans bogue. Le problème avec cette approche est la maintenabilité - nous devons réimplémenter cette fonctionnalité pour chaque proxy que nous utilisons. Sur une variante de cette réponse, nous verrons comment utiliser la composition plutôt que l'héritage pour rendre cette technique générique.Premier essai
Il existe différentes implémentations pour l'
IDisposable
implémentation, mais pour les besoins de l'argument, nous utiliserons une adaptation de celle utilisée par la réponse actuellement acceptée .Armé des classes ci-dessus, nous pouvons maintenant écrire
Cela nous permet de consommer notre service en utilisant le
using
bloc:Rendre ce générique
Tout ce que nous avons fait jusqu'à présent est de reformuler la solution de Tomas . Ce qui empêche ce code d'être générique, c'est le fait que la
ProxyWrapper
classe doit être réimplémentée pour chaque contrat de service que nous voulons. Nous allons maintenant regarder une classe qui nous permet de créer ce type dynamiquement en utilisant IL:Avec notre nouvelle classe d'aide, nous pouvons maintenant écrire
Notez que vous pouvez également utiliser la même technique (avec de légères modifications) pour les clients générés automatiquement héritant de
ClientBase<>
(au lieu d'utiliserChannelFactory<>
), ou si vous souhaitez utiliser une implémentation différente deIDisposable
pour fermer votre chaîne.la source
J'aime cette façon de fermer la connexion:
la source
J'ai écrit une classe de base simple qui gère cela. Il est disponible sous forme de package NuGet et il est assez facile à utiliser.
la source
Cela permet donc d'écrire des instructions de retour bien:
la source
Je voudrais ajouter l'implémentation de Service de la réponse de Marc Gravell pour le cas d'utilisation de ServiceClient au lieu de ChannelFactory.
la source
Pour les personnes intéressées, voici une traduction VB.NET de la réponse acceptée (ci-dessous). Je l'ai affiné un peu par souci de concision, en combinant certains des conseils d'autres dans ce fil.
J'avoue que c'est hors sujet pour les balises d'origine (C #), mais comme je n'ai pas pu trouver une version VB.NET de cette bonne solution, je suppose que d'autres chercheront également. La traduction de Lambda peut être un peu délicate, donc je voudrais éviter à quelqu'un le problème.
Notez que cette implémentation particulière offre la possibilité de configurer le
ServiceEndpoint
au moment de l'exécution.Code:
Usage:
la source
Notre architecture système utilise souvent le cadre Unity IoC pour créer des instances de ClientBase, il n'y a donc aucun moyen sûr de faire en sorte que les autres développeurs utilisent même
using{}
blocs. Afin de le rendre aussi infaillible que possible, j'ai créé cette classe personnalisée qui étend ClientBase et gère la fermeture du canal lors de la suppression ou de la finalisation au cas où quelqu'un ne disposerait pas explicitement de l'instance créée par Unity.Il y a aussi des choses à faire dans le constructeur pour configurer le canal pour les informations d'identification personnalisées et tout ça, donc c'est ici aussi ...
Un client peut alors simplement:
Et l'appelant peut effectuer l'une des opérations suivantes:
la source
J'ai référé quelques réponses sur ce post et l'ai personnalisé selon mes besoins.
Je voulais pouvoir faire quelque chose avec le client WCF avant de l'utiliser pour la
DoSomethingWithClient()
méthode.Voici la classe d'assistance:
Et je peux l'utiliser comme:
la source
J'ai mon propre wrapper pour un canal qui implémente Dispose comme suit:
Cela semble bien fonctionner et permet d'utiliser un bloc using.
la source
L'assistant suivant permet d'appeler
void
et de méthodes non nulles. Usage:La classe elle-même est:
la source
Remplacez Dispose () du client sans avoir besoin de générer une classe proxy basée sur ClientBase, également sans avoir besoin de gérer la création et la mise en cache des canaux ! (Notez que WcfClient n'est pas une classe ABSTRACT et est basé sur ClientBase)
la source
Ma méthode consiste à créer une classe héritée qui implémente explicitement IDisposable. Ceci est utile pour les personnes qui utilisent l'interface graphique pour ajouter la référence de service (Ajouter une référence de service). Je laisse simplement tomber cette classe dans le projet faisant la référence de service et l'utilise à la place du client par défaut:
Remarque: Il s'agit simplement d'une implémentation simple de disposer, vous pouvez implémenter une logique de disposition plus complexe si vous le souhaitez.
Vous pouvez ensuite remplacer tous vos appels passés avec le client de service régulier par les clients sûrs, comme ceci:
J'aime cette solution car elle ne nécessite pas d'avoir accès aux définitions d'interface et je peux utiliser le
using
instruction comme je m'y attendais tout en permettant à mon code de ressembler plus ou moins à la même chose.Vous devrez toujours gérer les exceptions qui peuvent être levées comme indiqué dans d'autres commentaires de ce fil.
la source
Vous pouvez également utiliser un
DynamicProxy
pour étendre laDispose()
méthode. De cette façon, vous pourriez faire quelque chose comme:la source