Quelle est la meilleure solution de contournement pour le problème de bloc `using` du client WCF?

J’aime instancier mes clients de service WCF dans un bloc using , car c’est à peu près le moyen standard d’utiliser des 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 bloc using pourrait masquer les erreurs qui entraîneraient le client à restr dans un état défaillant (comme un problème de délai d’attente ou 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 bloc using et à instancier plutôt vos clients et à les utiliser comme ceci:

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

Comparé au bloc d’ using , 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, comme celle-ci sur IServiceOriented. Vous commencez avec:

 public delegate void UseServiceDelegate(T proxy); public static class Service { public static ChannelFactory _channelFactory = new ChannelFactory(""); public static void Use(UseServiceDelegate 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.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 bloc d’ using .

La solution de contournement que je tente actuellement d’utiliser, j’ai tout d’abord lu sur blog.davidbarret.net . Fondamentalement, vous remplacez la méthode Dispose() du client où que vous l’utilisiez. Quelque chose comme:

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

Cela semble pouvoir autoriser using nouveau le bloc d’ using sans risquer de masquer une exception d’état défaillant.

Alors, y a-t-il d’autres pièges à éviter pour utiliser ces solutions de rechange? Quelqu’un at-il trouvé quelque chose de mieux?

En fait, même si j’ai blogué (voir la réponse de Luke ), je pense que c’est mieux que mon wrapper IDisposable. Code typique:

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

(modifier par commentaires)

Puisque Use renvoie void, le moyen le plus simple de gérer les valeurs de retour est d’ Use une variable capturée:

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

É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 peut s’y attendre avec un object jetable. Cependant, comme l’a fait remarquer @Brian, cette solution contient une condition de concurrence en ce sens que l’État peut ne pas être défaillant lorsqu’il est vérifié, mais pourrait être au moment où Close () est appelé, auquel cas CommunicationException se produit toujours.

Donc, pour contourner ce problème, j’ai employé une solution qui combine le meilleur des deux mondes.

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

J’ai écrit une fonction d’ordre supérieur pour que cela fonctionne correctement. Nous avons utilisé cela dans plusieurs projets et cela semble fonctionner très bien. C’est comme cela que les choses auraient dû être faites dès le début, sans le paradigme “d’utilisation”, etc.

 TReturn UseService(Func code) { var chanFactory = GetCachedFactory(); TChannel channel = chanFactory.CreateChannel(); bool error = true; try { TReturn result = code(channel); ((IClientChannel)channel).Close(); error = false; return result; } finally { if (error) { ((IClientChannel)channel).Abort(); } } } 

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 comme dans votre exemple. Dans certains projets, nous écrivons des méthodes d’aide fortement typées, nous finissons donc par écrire des choses comme “Wcf.UseFooService (f => f …)”.

Je trouve ça assez élégant, tout compte fait. Y a-t-il un problème particulier que vous avez rencontré?

Cela permet de twigr d’autres fonctionnalités astucieuses. Par exemple, sur un site, le site s’authentifie auprès du service pour le compte de l’utilisateur connecté. (Le site n’a pas d’identifiant par lui-même.) En écrivant notre propre assistant de méthode «UseService», nous pouvons configurer la fabrique de canaux comme nous le souhaitons, etc. Nous n’utilisons pas non plus les proxys générés .

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

 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(); } 

Informations supplémentaires Tant de personnes 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écharger l’échantillon: C # ou VB

Considérant qu’il y a tellement de problèmes concernant l’utilisation de l’énoncé , (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 juste le sucer et implémenter les clients WCF de cette manière verbeuse (mais fiable) pour mes applications serveur.

Échecs facultatifs supplémentaires à attraper

De nombreuses exceptions dérivent de CommunicationException et je ne pense pas que la plupart de ces exceptions doivent être retentées. J’ai parcouru chaque exception sur MSDN et trouvé une courte liste des exceptions TimeOutException (en plus de TimeOutException ci-dessus). Faites-moi savoir si j’ai manqué une exception qui devrait ê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 un code banal à écrire. Je préfère actuellement cette réponse et ne vois aucun “piratage” dans ce code qui pourrait causer des problèmes plus tard.

J’ai finalement trouvé des étapes solides vers une solution propre à ce problème.

Cet outil personnalisé étend WCFProxyGenerator pour fournir un proxy de gestion des exceptions. Il génère un proxy supplémentaire appelé ExceptionHandlingProxy qui hérite de ExceptionHandlingProxyBase , ce dernier implémentant la fonctionnalité du proxy. Le résultat est que vous pouvez choisir d’utiliser le proxy par défaut qui hérite de ClientBase ou ExceptionHandlingProxy qui encapsule la gestion de la durée de vie de la fabrique de canaux et du canal. ExceptionHandlingProxy respecte vos sélections dans la boîte de dialog Ajouter une référence de service en ce qui concerne les méthodes asynchrones et les types de collecte.

Codeplex a un projet appelé Exception Handling WCF Proxy Generator . 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 dispose de fonctionnalités intéressantes pour gérer les canaux défectueux, les délais d’expiration et leur élimination en toute sécurité. Il y a une excellente vidéo ici appelée ExceptionHandlingProxyWrapper expliquant exactement comment cela fonctionne.

Vous pouvez utiliser à nouveau en toute sécurité l’instruction Using , et si le canal est défaillant sur une requête (TimeoutException ou CommunicationException), le wrapper réinitialisera le canal défaillant et relancera la requête. Si cela échoue, il appelle la commande Abort() et supprime le proxy et relance l’exception. Si le service lève un code FaultException , il cessera de s’exécuter et le proxy sera annulé en toute sécurité en lançant l’exception correcte comme prévu.

Sur la base des réponses de Marc Gravell, MichaelGG et Matt Davis, nos développeurs ont proposé ce qui suit:

 public static class UsingServiceClient { public static void Do(TClient client, Action execute) where TClient : class, ICommunicationObject { try { execute(client); } finally { client.DisposeSafely(); } } public static void DisposeSafely(this ICommunicationObject client) { if (client == null) { return; } bool success = false; try { if (client.State != CommunicationState.Faulted) { client.Close(); success = true; } } finally { if (!success) { client.Abort(); } } } } 

Exemple d’utilisation:

 ssortingng result = ssortingng.Empty; UsingServiceClient.Do( new MyServiceClient(), client => result = client.GetServiceResult(parameters)); 

C’est aussi proche de la syntaxe “using” que possible, vous n’avez pas à renvoyer 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.

En outre, vous pouvez l’utiliser avec les ClientBase 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.

@ Marc Gravell

Ne serait-il pas correct d’utiliser ceci:

 public static TResult Using(this T client, Func work) where T : ICommunicationObject { try { var result = work(client); client.Close(); return result; } catch (Exception e) { client.Abort(); throw; } } 

Ou, la même chose (Func) en cas de Service.Use

Celles-ci faciliteraient le retour des variables.

Qu’est-ce que c’est?

Ceci est la version CW de la réponse acceptée mais avec (ce que je considère complet) la gestion des exceptions incluse.

La réponse acceptée fait référence à ce site qui n’est plus là . Pour vous éviter des problèmes, j’inclus les parties les plus pertinentes ici. De plus, je l’ai légèrement modifié pour inclure la gestion des nouvelles tentatives d’exception pour gérer ces délais d’attente réseau fastidieux.

Utilisation du client WCF simple

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 = (IClientChannel)_channelFactory.CreateChannel(); bool success = false; Exception mostRecentEx = null; int millsecondsToSleep = 1000; for(int i=0; i<5; i++) // Attempt a maximum of 5 times { try { codeBlock((T)proxy); proxy.Close(); success = true; break; } // 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 ) { // 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 ; } } if (success == false && mostRecentEx != null) { proxy.Abort(); throw new Exception("WCF call failed after 5 retries.", mostRecentEx ); } } } 

PS: J'ai fait de cet article un wiki de communauté. Je ne collecterai pas de "points" à partir de cette réponse, mais je préfère que vous en preniez note si vous êtes d'accord avec la mise en œuvre, ou si vous le modifiez pour le rendre meilleur.

Vous trouverez ci-dessous une version améliorée de la source à partir de la question et étendue pour mettre en cache plusieurs fabriques de canaux et tenter de rechercher le noeud final dans le fichier de configuration par nom de contrat.

Il utilise .NET 4 (spécifiquement: contravariance, LINQ, var ):

 ///  /// Delegate type of the service method to perform. ///  /// The service proxy. /// The type of service to use. internal delegate void UseServiceDelegate(T proxy); ///  /// Wraps using a WCF service. ///  /// The type of service to use. internal static class Service { ///  /// A dictionary to hold looked-up endpoint names. ///  private static readonly IDictionary cachedEndpointNames = new Dictionary(); ///  /// A dictionary to hold created channel factories. ///  private static readonly IDictionary> cachedFactories = new Dictionary>(); ///  /// Uses the specified code block. ///  /// The code block. internal static void Use(UseServiceDelegate codeBlock) { var factory = GetChannelFactory(); var proxy = (IClientChannel)factory.CreateChannel(); var success = false; try { using (proxy) { codeBlock((T)proxy); } success = true; } finally { if (!success) { proxy.Abort(); } } } ///  /// Gets the channel factory. ///  /// The channel factory. private static ChannelFactory GetChannelFactory() { lock (cachedFactories) { var endpointName = GetEndpointName(); if (cachedFactories.ContainsKey(endpointName)) { return cachedFactories[endpointName]; } var factory = new ChannelFactory(endpointName); cachedFactories.Add(endpointName, factory); return factory; } } ///  /// Gets the name of the endpoint. ///  /// The name of the endpoint. private static ssortingng GetEndpointName() { var type = typeof(T); var fullName = type.FullName; lock (cachedFactories) { if (cachedEndpointNames.ContainsKey(type)) { return cachedEndpointNames[type]; } var serviceModel = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).SectionGroups["system.serviceModel"] as ServiceModelSectionGroup; if ((serviceModel != null) && !ssortingng.IsNullOrEmpty(fullName)) { foreach (var endpointName in serviceModel.Client.Endpoints.Cast().Where(endpoint => fullName.EndsWith(endpoint.Contract)).Select(endpoint => endpoint.Name)) { cachedEndpointNames.Add(type, endpointName); return endpointName; } } } throw new InvalidOperationException("Could not find endpoint element for type '" + fullName + "' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this name could be found in the client element."); } } 

Un emballage comme celui-ci fonctionnerait:

 public class ServiceClientWrapper : IDisposable { private ServiceType _channel; public ServiceType Channel { get { return _channel; } } private static ChannelFactory _channelFactory; public ServiceClientWrapper() { if(_channelFactory == null) // Given that the endpoint name is the same as FullName of contract. _channelFactory = new ChannelFactory(typeof(T).FullName); _channel = _channelFactory.CreateChannel(); ((IChannel)_channel).Open(); } public void Dispose() { try { ((IChannel)_channel).Close(); } catch (Exception e) { ((IChannel)_channel).Abort(); // TODO: Insert logging } } } 

Cela devrait vous permettre d’écrire du code comme:

 ResponseType response = null; using(var clientWrapper = new ServiceClientWrapper()) { var request = ... response = clientWrapper.Channel.MyServiceCall(request); } // Use your response object. 

Le wrapper pourrait bien sûr prendre plus d’exceptions si cela est nécessaire, mais le principe rest le même.

J’ai utilisé le proxy dynamic Castle pour résoudre le problème Dispose (), et j’ai également implémenté le rafraîchissement automatique du canal lorsqu’il est inutilisable. Pour l’utiliser, vous devez créer une nouvelle interface qui hérite de votre contrat de service et IDisposable. Le proxy dynamic implémente cette interface et encapsule un canal WCF:

 Func createChannel = () => ChannelFactory .CreateChannel(new NetTcpBinding(), new EndpointAddress(uri)); var factory = new WcfProxyFactory(); var proxy = factory.Create(createChannel); proxy.HelloWorld(); 

J’aime cela puisque vous pouvez injecter des services WCF sans que les consommateurs aient à se soucier des détails de WCF. Et il n’y a pas d’autre cruauté que les autres solutions.

Jetez un oeil sur le code, il est en fait assez simple: proxy dynamic WCF

Si vous n’avez pas besoin d’ IoC ou utilisez un client généré automatiquement (Service Reference), vous pouvez simplement utiliser un wrapper pour gérer la fermeture et laisser le GC prendre la firebase database lorsqu’elle est dans un état sûr qui ne générera aucune exception. Le CPG appellera Dispose en serviceclient, ce qui appellera Close . Comme il est déjà fermé, il ne peut causer aucun dommage. Je l’utilise sans problèmes dans le code de production.

 public class AutoCloseWcf : IDisposable { private ICommunicationObject CommunicationObject; public AutoDisconnect(ICommunicationObject CommunicationObject) { this.CommunicationObject = CommunicationObject; } public void Dispose() { if (CommunicationObject == null) return; try { if (CommunicationObject.State != CommunicationState.Faulted) { CommunicationObject.Close(); } else { CommunicationObject.Abort(); } } catch (CommunicationException ce) { CommunicationObject.Abort(); } catch (TimeoutException toe) { CommunicationObject.Abort(); } catch (Exception e) { CommunicationObject.Abort(); //Perhaps log this } finally { CommunicationObject = null; } } } 

Ensuite, lorsque vous accédez au serveur, vous créez le client et utilisez en using l’autodisconect:

 var Ws = new ServiceClient("netTcpEndPointName"); using (new AutoCloseWcf(Ws)) { Ws.Open(); Ws.Test(); } 

Utilisez une méthode d’extension:

 public static class CommunicationObjectExtensions { public static TResult MakeSafeServiceCall(this TService client, Func method) where TService : ICommunicationObject { TResult result; try { result = method(client); } finally { try { client.Close(); } catch (CommunicationException) { client.Abort(); // Don't care about these exceptions. The call has completed anyway. } catch (TimeoutException) { client.Abort(); // Don't care about these exceptions. The call has completed anyway. } catch (Exception) { client.Abort(); throw; } } return result; } } 

Résumé

En utilisant les techniques décrites dans cette réponse, vous pouvez utiliser un service WCF dans un bloc utilisant la syntaxe suivante:

 var channelFactory = new ChannelFactory(""); var serviceHelper = new ServiceHelper(channelFactory); var proxy = serviceHelper.CreateChannel(); using (proxy as IDisposable) { proxy.DoWork(); } 

Vous pouvez bien sûr l’adapter 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 reprenant le canal qui implémente correctement le modèle jetable.


Détails

Toutes les réponses données jusqu’ici traitent du problème de contournement du “bug” dans l’implémentation IDisposable de WCF Channel. La réponse qui semble offrir le modèle de programmation le plus concis (vous permettant d’utiliser le bloc using pour disposer sur des ressources non gérées) est la suivante : le proxy est modifié pour implémenter IDisposable avec une implémentation sans bogue. Le problème avec cette approche est la maintenabilité – nous devons réimplémenter cette fonctionnalité pour tous les proxy que nous utilisons. Sur une variante de cette réponse, nous verrons comment utiliser la composition plutôt que l’inheritance pour rendre cette technique générique.

Premier essai

Il semble qu’il y ait diverses implémentations pour l’implémentation IDisposable , mais par souci d’argument, nous utiliserons une adaptation de celle utilisée par la réponse actuellement acceptée .

 [ServiceContract] public interface IMyService { [OperationContract] void DoWork(); } public class ProxyDisposer : IDisposable { private IClientChannel _clientChannel; public ProxyDisposer(IClientChannel clientChannel) { _clientChannel = clientChannel; } public void Dispose() { var success = false; try { _clientChannel.Close(); success = true; } finally { if (!success) _clientChannel.Abort(); _clientChannel = null; } } } public class ProxyWrapper : IMyService, IDisposable { private IMyService _proxy; private IDisposable _proxyDisposer; public ProxyWrapper(IMyService proxy, IDisposable disposable) { _proxy = proxy; _proxyDisposer = disposable; } public void DoWork() { _proxy.DoWork(); } public void Dispose() { _proxyDisposer.Dispose(); } } 

Armé avec les classes ci-dessus, nous pouvons maintenant écrire

 public class ServiceHelper { private readonly ChannelFactory _channelFactory; public ServiceHelper(ChannelFactory channelFactory ) { _channelFactory = channelFactory; } public IMyService CreateChannel() { var channel = _channelFactory.CreateChannel(); var channelDisposer = new ProxyDisposer(channel as IClientChannel); return new ProxyWrapper(channel, channelDisposer); } } 

Cela nous permet de consumr notre service en utilisant le bloc using :

 ServiceHelper serviceHelper = ...; var proxy = serviceHelper.CreateChannel(); using (proxy as IDisposable) { proxy.DoWork(); } 

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 classe ProxyWrapper doit être ré-implémentée pour chaque contrat de service que nous souhaitons. We will now look at a class that allows us to create this type dynamically using IL:

 public class ServiceHelper { private readonly ChannelFactory _channelFactory; private static readonly Func _channelCreator; static ServiceHelper() { /** * Create a method that can be used generate the channel. * This is effectively a comstackd verion of new ProxyWrappper(channel, channelDisposer) for our proxy type * */ var assemblyName = Guid.NewGuid().ToSsortingng(); var an = new AssemblyName(assemblyName); var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run); var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName); var proxyType = CreateProxyType(moduleBuilder, typeof(T), typeof(IDisposable)); var channelCreatorMethod = new DynamicMethod("ChannelFactory", typeof(T), new[] { typeof(T), typeof(IDisposable) }); var ilGen = channelCreatorMethod.GetILGenerator(); var proxyVariable = ilGen.DeclareLocal(typeof(T)); var disposableVariable = ilGen.DeclareLocal(typeof(IDisposable)); ilGen.Emit(OpCodes.Ldarg, proxyVariable); ilGen.Emit(OpCodes.Ldarg, disposableVariable); ilGen.Emit(OpCodes.Newobj, proxyType.GetConstructor(new[] { typeof(T), typeof(IDisposable) })); ilGen.Emit(OpCodes.Ret); _channelCreator = (Func)channelCreatorMethod.CreateDelegate(typeof(Func)); } public ServiceHelper(ChannelFactory channelFactory) { _channelFactory = channelFactory; } public T CreateChannel() { var channel = _channelFactory.CreateChannel(); var channelDisposer = new ProxyDisposer(channel as IClientChannel); return _channelCreator(channel, channelDisposer); } /** * Creates a dynamic type analogous to ProxyWrapper, implementing T and IDisposable. * This method is actually more generic than this exact scenario. * */ private static Type CreateProxyType(ModuleBuilder moduleBuilder, params Type[] interfacesToInjectAndImplement) { TypeBuilder tb = moduleBuilder.DefineType(Guid.NewGuid().ToSsortingng(), TypeAtsortingbutes.Public | TypeAtsortingbutes.Class); var typeFields = interfacesToInjectAndImplement.ToDictionary(tf => tf, tf => tb.DefineField("_" + tf.Name, tf, FieldAtsortingbutes.Private)); #region Constructor var constructorBuilder = tb.DefineConstructor( MethodAtsortingbutes.Public | MethodAtsortingbutes.HideBySig | MethodAtsortingbutes.SpecialName | MethodAtsortingbutes.RTSpecialName, CallingConventions.Standard, interfacesToInjectAndImplement); var il = constructorBuilder.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0])); for (var i = 1; i < = interfacesToInjectAndImplement.Length; i++) { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg, i); il.Emit(OpCodes.Stfld, typeFields[interfacesToInjectAndImplement[i - 1]]); } il.Emit(OpCodes.Ret); #endregion #region Add Interface Implementations foreach (var type in interfacesToInjectAndImplement) { tb.AddInterfaceImplementation(type); } #endregion #region Implement Interfaces foreach (var type in interfacesToInjectAndImplement) { foreach (var method in type.GetMethods()) { var methodBuilder = tb.DefineMethod(method.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.Final | MethodAttributes.NewSlot, method.ReturnType, method.GetParameters().Select(p => p.ParameterType).ToArray()); il = methodBuilder.GetILGenerator(); if (method.ReturnType == typeof(void)) { il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, typeFields[type]); il.Emit(OpCodes.Callvirt, method); il.Emit(OpCodes.Ret); } else { il.DeclareLocal(method.ReturnType); il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, typeFields[type]); var methodParameterInfos = method.GetParameters(); for (var i = 0; i < methodParameterInfos.Length; i++) il.Emit(OpCodes.Ldarg, (i + 1)); il.Emit(OpCodes.Callvirt, method); il.Emit(OpCodes.Stloc_0); var defineLabel = il.DefineLabel(); il.Emit(OpCodes.Br_S, defineLabel); il.MarkLabel(defineLabel); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ret); } tb.DefineMethodOverride(methodBuilder, method); } } #endregion return tb.CreateType(); } } 

With our new helper class we can now write

 var channelFactory = new ChannelFactory(""); var serviceHelper = new ServiceHelper(channelFactory); var proxy = serviceHelper.CreateChannel(); using (proxy as IDisposable) { proxy.DoWork(); } 

Note that you could also use the same technique (with slight modifications) for auto-generated clients inheriting for ClientBase<> (instead of using ChannelFactory<> ), or if you want to use a different implementation of IDisposable to close your channel.

I like this way of closing connection:

 var client = new ProxyClient(); try { ... client.Close(); } finally { if(client.State != CommunicationState.Closed) client.Abort(); } 

I have written a simple base class that handles this. It’s available as a NuGet package and it’s quite easy to use.

 //MemberServiceClient is the class generated by SvcUtil public class MemberServiceManager : ServiceClientBase { public User GetUser(int userId) { return PerformServiceOperation(client => client.GetUser(userId)); } //you can also check if any error occured if you can't throw exceptions public bool TryGetUser(int userId, out User user) { return TryPerformServiceOperation(c => c.GetUser(userId), out user); } } 
 public static class Service { public static ChannelFactory ChannelFactory = new ChannelFactory("*"); public static TReturn Use(Func codeBlock) { var proxy = (IClientChannel)ChannelFactory.CreateChannel(); var success = false; try { var result = codeBlock((TChannel)proxy); proxy.Close(); success = true; return result; } finally { if (!success) { proxy.Abort(); } } } } 

So it allows to write return statements nicely:

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

I’d like to add implementation of Service from Marc Gravell’s answer for case of using ServiceClient instead of ChannelFactory.

 public interface IServiceConnector { void Connect(Action clientUsage); TResult Connect(Func channelUsage); } internal class ServiceConnector : IServiceConnector where TServiceInterface : class where TService : ClientBase, TServiceInterface, new() { public TResult Connect(Func channelUsage) { var result = default(TResult); Connect(channel => { result = channelUsage(channel); }); return result; } public void Connect(Action clientUsage) { if (clientUsage == null) { throw new ArgumentNullException("clientUsage"); } var isChanneldClosed = false; var client = new TService(); try { clientUsage(client); client.Close(); isChanneldClosed = true; } finally { if (!isChanneldClosed) { client.Abort(); } } } } 

For those interestd, here’s a VB.NET translation of the accepted answer (below). I’ve refined it a bit for brevity, combining some of the tips by others in this thread.

I admit it’s off-topic for the originating tags (C#), but as I wasn’t able to find a VB.NET version of this fine solution I assume that others will be looking as well. The Lambda translation can be a bit sortingcky, so I’d like to save someone the trouble.

Note that this particular implementation provides the ability to configure the ServiceEndpoint at runtime.


Code:

 Namespace Service Public NotInheritable Class Disposable(Of T) Public Shared ChannelFactory As New ChannelFactory(Of T)(Service) Public Shared Sub Use(Execute As Action(Of T)) Dim oProxy As IClientChannel oProxy = ChannelFactory.CreateChannel Try Execute(oProxy) oProxy.Close() Catch oProxy.Abort() Throw End Try End Sub Public Shared Function Use(Of TResult)(Execute As Func(Of T, TResult)) As TResult Dim oProxy As IClientChannel oProxy = ChannelFactory.CreateChannel Try Use = Execute(oProxy) oProxy.Close() Catch oProxy.Abort() Throw End Try End Function Public Shared ReadOnly Property Service As ServiceEndpoint Get Return New ServiceEndpoint( ContractDescription.GetContract( GetType(T), GetType(Action(Of T))), New BasicHttpBinding, New EndpointAddress(Utils.WcfUri.ToSsortingng)) End Get End Property End Class End Namespace 

Usage:

 Public ReadOnly Property Jobs As List(Of Service.Job) Get Disposable(Of IService).Use(Sub(Client) Jobs = Client.GetJobs(Me.Status)) End Get End Property Public ReadOnly Property Jobs As List(Of Service.Job) Get Return Disposable(Of IService).Use(Function(Client) Client.GetJobs(Me.Status)) End Get End Property 

Our system architecture often uses the Unity IoC framework to create instances of ClientBase so there’s no sure way to enforce that the other developers even use using{} blocks. In order to make it as fool-proof as possible, I made this custom class that extends ClientBase, and handles closing down the channel on dispose, or on finalize in case someone doesn’t explicitly dispose of the Unity created instance.

There is also stuff that needed to be done in the constructor to set up the channel for custom credentials and stuff, so that’s in here too…

 public abstract class PFServer2ServerClientBase : ClientBase, IDisposable where TChannel : class { private bool disposed = false; public PFServer2ServerClientBase() { // Copy information from custom identity into credentials, and other channel setup... } ~PFServer2ServerClientBase() { this.Dispose(false); } void IDisposable.Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } public void Dispose(bool disposing) { if (!this.disposed) { try { if (this.State == CommunicationState.Opened) this.Close(); } finally { if (this.State == CommunicationState.Faulted) this.Abort(); } this.disposed = true; } } } 

Then a client can simply:

 internal class TestClient : PFServer2ServerClientBase, ITest { public ssortingng TestMethod(int value) { return base.Channel.TestMethod(value); } } 

And the caller can do any of these:

 public SomeClass { [Dependency] public ITest test { get; set; } // Not the best, but should still work due to finalizer. public ssortingng Method1(int value) { return this.test.TestMethod(value); } // The good way to do it public ssortingng Method2(int value) { using(ITest t = unityContainer.Resolve()) { return t.TestMethod(value); } } } 

I referred few answers on this post and customized it as per my needs.

I wanted the ability to do something with WCF client before using it so the DoSomethingWithClient() method.

 public interface IServiceClientFactory { T DoSomethingWithClient(); } public partial class ServiceClient : IServiceClientFactory { public ServiceClient DoSomethingWithClient() { var client = this; // do somthing here as set client credentials, etc. //client.ClientCredentials = ... ; return client; } } 

Here is the helper class:

 public static class Service where TClient : class, ICommunicationObject, IServiceClientFactory, new() { public static TReturn Use(Func codeBlock) { TClient client = default(TClient); bool success = false; try { client = new TClient().DoSomethingWithClient(); TReturn result = codeBlock(client); client.Close(); success = true; return result; } finally { if (!success && client != null) { client.Abort(); } } } } 

And I can use it as:

 ssortingng data = Service.Use(x => x.GetData(7)); 

I have my own wrapper for a channel which implements Dispose as follows:

 public void Dispose() { try { if (channel.State == CommunicationState.Faulted) { channel.Abort(); } else { channel.Close(); } } catch (CommunicationException) { channel.Abort(); } catch (TimeoutException) { channel.Abort(); } catch (Exception) { channel.Abort(); throw; } } 

This seems to work well and allows a using block to be used.

The following helper allows to call void and non-void methods. Usage:

 var calculator = new WcfInvoker(() => new CalculatorClient()); var sum = calculator.Invoke(c => c.Sum(42, 42)); calculator.Invoke(c => c.RebootComputer()); 

The class itself is:

 public class WcfInvoker where TService : ICommunicationObject { readonly Func _clientFactory; public WcfInvoker(Func clientFactory) { _clientFactory = clientFactory; } public T Invoke(Func action) { var client = _clientFactory(); try { var result = action(client); client.Close(); return result; } catch { client.Abort(); throw; } } public void Invoke(Action action) { Invoke(client => { action(client); return null; }); } } 

Override the client’s Dispose() without the need to generate a proxy class based on ClientBase, also without the need to manage channel creation and caching ! (Note that WcfClient is not an ABSTRACT class and is based on ClientBase)

 // No need for a generated proxy class //using (WcfClient orderService = new WcfClient()) //{ // results = orderService.GetProxy().PlaceOrder(input); //} public class WcfClient : ClientBase, IDisposable where TService : class { public WcfClient() { } public WcfClient(ssortingng endpointConfigurationName) : base(endpointConfigurationName) { } public WcfClient(ssortingng endpointConfigurationName, ssortingng remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public WcfClient(ssortingng endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public WcfClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : base(binding, remoteAddress) { } protected virtual void OnDispose() { bool success = false; if ((base.Channel as IClientChannel) != null) { try { if ((base.Channel as IClientChannel).State != CommunicationState.Faulted) { (base.Channel as IClientChannel).Close(); success = true; } } finally { if (!success) { (base.Channel as IClientChannel).Abort(); } } } } public TService GetProxy() { return this.Channel as TService; } public void Dispose() { OnDispose(); } } 

My method of doing this has been to create an inherited class that explicitly implements IDisposable. This is useful for folks who use the gui to add the service reference ( Add Service Reference ). I just drop this class in the project making the service reference and use it instead of the default client:

 using System; using System.ServiceModel; using MyApp.MyService; // The name you gave the service namespace namespace MyApp.Helpers.Services { public class MyServiceClientSafe : MyServiceClient, IDisposable { void IDisposable.Dispose() { if (State == CommunicationState.Faulted) { Abort(); } else if (State != CommunicationState.Closed) { Close(); } // Further error checks and disposal logic as desired.. } } } 

Note: This is just a simple implementation of dispose, you can implement more complex dispose logic if you like.

You can then replace all your calls made with the regular service client with the safe clients, like this:

 using (MyServiceClientSafe client = new MyServiceClientSafe()) { var result = client.MyServiceMethod(); } 

I like this solution as it does not require me to have access to the Interface definitions and I can use the using statement as I would expect while allowing my code to look more or less the same.

You will still need to handle the exceptions which can be thrown as pointed out in other comments in this thread.

You could also use a DynamicProxy to extend the Dispose() method. This way you could do something like:

 using (var wrapperdProxy = new Proxy()) { // Do whatever and dispose of Proxy will be called and work properly. }