Introduction

One of the way tuning performance of WCF services is to use WCF proxy caching. There has been a long discussion on this subject. Bottom line is FW 4.0 supports caching proxies with some conditions, you may see details here.

In order to call WCF services, we need a proxy first. We can create one either at design time or run time. Let’s see each one

Design Time Proxy Creation

Once the services are ready and in run state, you can utilize svcutil.exe or Visual Studio’ service reference features to create a proxy. For example, in my prior post here, when added a service reference to the project, VS 2010  creates proxy and contracts classes behind as below:

 1: [System.Diagnostics.DebuggerStepThroughAttribute()]
 2: [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
 3: public partial class Service1Client : System.ServiceModel.ClientBase<ServiceApplicationTest.TargetService.IService1>, ServiceApplicationTest.TargetService.IService1 {
 4:     
 5:     public Service1Client() {
 6:     }
 7:     
 8:     public Service1Client(string endpointConfigurationName) : 
 9:             base(endpointConfigurationName) {
 10:     }
 11:     
 12:     public Service1Client(string endpointConfigurationName, string remoteAddress) : 
 13:             base(endpointConfigurationName, remoteAddress) {
 14:     }
 15:     
 16:     public Service1Client(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : 
 17:             base(endpointConfigurationName, remoteAddress) {
 18:     }
 19:     
 20:     public Service1Client(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 
 21:             base(binding, remoteAddress) {
 22:     }
 23:     
 24:     public string GetData(int value) {
 25:         return base.Channel.GetData(value);
 26:     }
 27: }

Notice that it is using Channel property of ClientBase<T> abstract class and here we don’t own this channel or its lifetime, once the call is done, proxy is gone (like a lyricSmile).   After that, I can call the proxy as simple as below from my unit test method:

 1: [TestMethod]
 2: public void GetDataTest()
 3: {
 4:     //TargetService.IService1 proxy = new TargetService.Service1Client("targetEP");
 5:     TargetService.IService1 proxy = new TargetService.Service1Client("routerEP");   
 6:     int value = 99;
 7:     string expected = string.Format("You entered: {0}", value);
 8:     string actual = proxy.GetData(value);
 9:     Assert.AreEqual(expected, actual);
 10: }

More details on this approach can be found here http://msdn.microsoft.com/en-us/library/ms733133.aspx

Run Time Proxy Generation

As a second approach, you can create your own channel through Channel Factory. Here is code to create a proxy with an  IType by using Factory Pattern.

   1: internal class ProxyFactory<IType>
   2: {      
   3:     static System.ServiceModel.Channels.Binding binding;
   4:  
   5:     public static ChannelFactory<IType> Create()
   6:     {
   7:         #region Getting Dynamic Values
   8:         EndpointAddress epa=null;
   9:         string fullName = typeof(IType).FullName.Replace("Contracts", "Services").Replace(".I", ".");
  10:         System.ServiceModel.Configuration.ServicesSection services = System.Configuration.ConfigurationManager.GetSection("system.serviceModel/services") as System.ServiceModel.Configuration.ServicesSection;
  11:  
  12:         System.ServiceModel.Configuration.ServiceElementCollection coll = services.Services;
  13:         string strBinding = string.Empty;
  14:         for (int i = 0; i < coll.Count; i++)
  15:         {
  16:             System.ServiceModel.Configuration.ServiceElement el = coll[i];
  17:             if (el.Name.Equals(fullName))
  18:             {
  19:                 epa = new EndpointAddress(el.Endpoints[0].Address.ToString());
  20:                 strBinding = el.Endpoints[0].Binding;
  21:                 
  22:                 switch(strBinding)
  23:                 {
  24:                     case "basicHttpBinding":
  25:                         binding = new BasicHttpBinding();
  26:                         break;
  27:                     case "wsHttpBinding":
  28:                         binding = new WSHttpBinding();
  29:                         break;
  30:                     case "netTcpBinding":
  31:                         binding = new NetTcpBinding();
  32:                         break;
  33:                     case "netmsmqBinding":
  34:                         binding = new NetMsmqBinding();
  35:                         break;
  36:                     default:
  37:                         throw new NotImplementedException(strBinding + " not implemented.");
  38:                 }
  39:                 break;
  40:             }
  41:         }
  42:         #endregion
  43:         
  44:         return new ChannelFactory<IType>(binding, epa);           
  45:     }
  46: }

As we know, WCF clients communicate to services based on contracts (hand-shake) and that is the minimal piece to be shared between publisher and consumer. Here, what we do is to make an assembly including both contracts and proxy creation class(es) and add reference to it from the client.

Pros and cons of using Run time proxy

  • Pros
    • No need to go through refreshing service references if contract or address changed
    • You have your own transparent proxy, you can control it
    • All is implemented in an assembly or assemblies, easy to share
    • Clean config
  • Cons
    • Service calls are per service contracts

Proxy Caching

In either way, generating a proxy for each call is an expensive operation (constructing channel, opening it, closing, etc.). Instead we can cache each channel once they are created and utilize them for the next calls, as pointed out earlier, there are some cases, caching is already supported within the framework.

Above, the 2nd option, generating proxy at run time is not calling Open() explicitly, so we know it is not going to use caching by default. Remember we are to cache ChannelFactory that the proxy is created based on. Here is a proxy manager class which adds or removes the underline ChannelFactory to/from dictionary object.

   1: internal static class ProxyManager<IType>
   2: {
   3:     internal static ConcurrentDictionary<string, ChannelFactory<IType>> proxies = new ConcurrentDictionary<string, ChannelFactory<IType>>();
   4:  
   5:     internal static IType GetProxy(string key)
   6:     {
   7:         return proxies.GetOrAdd(key, m => ProxyFactory<IType>.Create()).CreateChannel();
   8:     }
   9:  
  10:     internal static bool RemoveProxy(string key)
  11:     {
  12:         ChannelFactory<IType> proxy;
  13:         return proxies.TryRemove(key, out proxy);
  14:     }
  15: }

Hope it suits your case.

Conclusion

WCF proxies are expensive to generate/dispose. FW 4.0 supports caching proxies with some conditions. In this post, I have leveraged ConcurrentDictionary object (FW 4.0) to cache each channel factory from which proxy is generated in a thread-safe manner to tune WCF service performances one step ahead.