Gal Segal's Blog

Thoughts of a programmer with a soul

Dec 14

WCF Client Proxy Creation Process

Tags:

WCF Client Proxy Creation Process

WCF enables us to consume a service in 2 ways: adding a service reference or by creating the proxy yourself. Both methods do the same thing – create a proxy class and and using it with the channel factory. Both cases are good, and its a matter of preference which one you use. I tend to use the second one – I prefer creating my proxy and add reference to the service dlls in the consuming app.

In this post I want to show you how to do it correctly. Generally this is a simple process, but when working in high performance environments, we want to be as efficient as possible with creation and consumption of the service from the client application. The basic idea is that creating the channel factory takes time, but WCF will cache this object in the MRU (see here  for more details) and the next invocations will be faster.

A good infrastructure should expose a simple interface, and hide all the “dirty” processes, living the programmer a light and simple access. This is what to “ProxyHelper” class does in this process.

First, we need a service:

[ServiceContract]
public interface ITestService
{
 [OperationContract]
 string DoWork();
}

public class TestService : ITestService
{
 public string DoWork()
 {
   return "Hello";
 }
}

Than we create the proxy for this service, by implementing ClientBase<T> abstract class and the service contract:

public class TestServiceProxy : ClientBase<ITestService>, ITestService
{
#region Ctor

public TestServiceProxy()
{

}

public TestServiceProxy(string endpointConfigurationName)
 : base(endpointConfigurationName)
{

}

public TestServiceProxy(string endpointConfigurationName, string remoteAddress)
 : base(endpointConfigurationName, remoteAddress)
{

}

public TestServiceProxy(string endpointConfigurationName, EndpointAddress remoteAddress)
 : base(endpointConfigurationName, remoteAddress)
{

}

public TestServiceProxy(Binding binding, EndpointAddress remoteAddress) :
 base(binding, remoteAddress)
{

}

#endregion

#region Methods

public string DoWork()
{
   return base.Channel.DoWork();
}

#endregion
}

In the consuming application we add reference to the service contract and proxy dll.
Now, in order to create the proxy instance efficiently and safely, we need to implement a few things:

  1. Wrap the proxy instance in a “using” statement
  2. Catch all exception types and abort the operation.

This can be done nicely with a proxy helper that will do all the work for us:

public static class ProxyHelper
{
 public static void InvokeProxyMethod<TProxy>(Action<TProxy> action)
 where TProxy : class, IDisposable, ICommunicationObject
 {
   InvokeMethod<TProxy>(action);
 }

public static void InvokeProxyMethod<TProxy>(string endpointConfigurationName, Action<TProxy> action)
 where TProxy : class, IDisposable, ICommunicationObject
 {
   InvokeMethod<TProxy>(action, endpointConfigurationName);
 }

public static void InvokeProxyMethod<TProxy>(string endpointConfigurationName, string address, Action<TProxy> action)
 where TProxy : class, IDisposable, ICommunicationObject
 {
   InvokeMethod<TProxy>(action, endpointConfigurationName, address);
 }

public static void InvokeProxyMethod<TProxy>(Binding binding, EndpointAddress address, Action<TProxy> action)
 where TProxy : class, IDisposable, ICommunicationObject
 {
   InvokeMethod<TProxy>(action, binding, address);
 }

private static void InvokeMethod<TProxy>(Action<TProxy> action, params object[] args)
 where TProxy : class, IDisposable, ICommunicationObject
 {
 using (var proxy = Resolve<TProxy>(args))
 {
   bool success = false;
   proxy.Open();
   try
   {
     action(proxy);
     success = true;
   }
   catch (CommunicationException ex)
   {
     throw new Exception(string.Format("CommunicationException - Failed to invoke '{0}' proxy", proxy.GetType().Name), ex);
   }
   catch (TimeoutException ex)
   {
     throw new Exception(string.Format("TimeoutException - Failed to invoke '{0}' proxy", proxy.GetType().Name), ex);
   }
   catch (Exception ex)
   {
     throw new Exception(string.Format("GeneralException - Failed to invoke '{0}' proxy", proxy.GetType().Name), ex);
   }
   finally
   {
     if (!success)
     {
       proxy.Abort();
     }
   }
  }
 }

private static T Resolve<T>(params object[] args)
{
   Type resolverType = typeof(T);
   if (resolverType == null)
   {
     throw new Exception(string.Format("type '{0}' does not exits", resolverType));
   }
   try
   {
     return (T)Activator.CreateInstance(resolverType, args);
   }
   catch (Exception ex)
   {
     throw new Exception(string.Format("could not create an instance of type '{0}'", resolverType), ex);
   }
 }
}

The helper class creates an instance of the proxy, utilizing all proxy’s constructors, and enables the consuming class to define an action to be performed on the proxy. It wraps the proxy method invocation with the needed “try catch” options.
A simple usage for this helper is as follows: (this example uses the Binding and EndpointAddress constructor of the ClientBase<T> class, but you can use any other option)

public class TestServiceConsumer
{
  public string DoWork()
  {
    Binding binding = new WSHttpBinding();
    EndpointAddress address = new EndpointAddress("http://192.168.11.78:800/InnerOAuthService.svc");
    string response = string.Empty;
    ProxyHelper.InvokeProxyMethod<TestServiceProxy>(binding, address, (proxy) =>
    {
      response = proxy.DoWork();
     });
    return response;
   }
 }
Back to top