Calling WCF Services: A better way than generating a proxy

Introduction

This article deals with replacing the auto generated proxy class in
WCF with a generic class such that if the interface of the service
contract is known, there is no need of ever generating a proxy class,
which ends up improving developer productivity.

Audience

It is assumed that the audience is aware of WCF, operation
contracts, data contracts, WCF Channel, Lambda Expressions, and
Generics to understand the code. If however you just want to use the
code, basic knowledge of C# is required.

Background

It is only in a dream that we can have all our interfaces closed
before development phase. In practice, interfaces change because the
method signature must change, or a new requirement comes in, or some
other unanticipated task pops up.

The proxy is usually generated after defining the interface, and it
is required that all associated code such as implementers of the
interface compile. There are multiple instances where some interface
changes need to be commented to get the proxy and then regenerate the
proxy at least three times. This problem is compounded as the number of
implementers of the interface increases.

Using the proxy also generates equivalent classes of the data
contract for which adapters need to be written in some extreme
programming requirements.

Solution

These problems can be eliminated by generation of a proxy. To achieve this, two technologies are used:

  1. Generics – This helps us to specify a type at runtime
  2. Lambda Expressions – This helps us to specify a code block (name of
    the operation contract method) to be passed to a function (to execute
    the code)

Code

This section jumps straight into the code. The first section
describes the code of the service and how it will be used. This is
followed by code of the proxy helper and how to use it. In this
article, the code has been written in Visual studio 2010 using the .NET
3.5 framework. In case the client can only be created in .NET 2.0, the
client code may be written using anonymous delegates.

Code Organization

WCFInterface – This class contains the interface of the
service contract along with the data contracts. In a regular WCF
project, these are part of the service implementation, but in this
scenario, they have been separated. This project will be referenced by
the client.

  • WCFService1 – This is the implementation of the service contract by a class.
  • WCFProxy – This is the proxy helper class whose job is to replace the proxy.
  • WCFClient – This is the WCF client which uses the proxy.

It should be noted that channel definition on both the server and the client remains unchanged and will be picked from the .config file.

The Service

The service in this code as visible is the default WCF service
generated by the template. The only change done to the code is
separation of the service implementation from the service contract in a
separate project. The WCFInterface project is referred to by both the
client and WCFService1.

Calling Sync

This section describes the code to call the WCF service in a synchronous call.

The proxy helper

This is a delegate which will be used by the client to specify the
method name on the interface. This delegate says tell us the name of
the code of type T.

Collapse
/// <summary> 
/// This delegate describes the method on the interface to be called.
/// </summary>
/// <typeparam name="T">This is the type of the interface</typeparam>
/// <param name="proxy">This is the method.</param>

public delegate void UseServiceDelegate<T>(T proxy);

This is a simple method. It creates a channel using the channel
factory for the specified WCF end point and opens the channel. It then
calls the code block – which is the method on type T as told by UseServiceDelegate.

Collapse
/// <summary> 
/// Invokes the method on the WCF interface with the given end point to
/// create a channel
/// Usage
/// new ProxyHelper<InterfaceName>().Use(serviceProxy =>
/// {
/// value = serviceProxy.MethodName(params....);
/// }, "WCFEndPoint");
/// </summary>
/// <param name="codeBlock">The WCF interface method of interface of type T
/// </param>
/// <param name="WCFEndPoint">The end point.</param>
public void Use(UseServiceDelegate<T> codeBlock, string WCFEndPoint)
{
try
{
//Create an instance of proxy
this.proxy = GetChannelFactory(WCFEndPoint).CreateChannel() as IClientChannel;
if (this.proxy != null)
{
//open the proxy
this.proxy.Open();
//Call the method
codeBlock((T)this.proxy);
this.proxy.Close();
}
}

catch (CommunicationException communicationException)
....

The client

This is the client to the above code. We tell the LamdaProxyHelper that we want a proxy for type IService1 and then call the Use method. In the Lambda Expression, we call the method GetData and return the value in the variable value (Lambda Expressions allow us to access the local variables).

Collapse
//using a proxy for the interface IService1 call the method GetData 
//call a sync method
new LamdaProxyHelper<IService1>().Use(serviceProxy =>
{
//save the return value in value
value = serviceProxy.GetData(7);
}, "WCFEndPoint");//use the end point name WCFEndPoint for this proxy

I would suggest that at this point of time, you should try this code
and understand this completely before moving to the next piece which is
calling async.

Calling Async

Proxy helper

This code follows a similar pattern; here, the delegate tells that
it will be sending an additional parameter while executing. This is the
object obj. This is sent because it helps during the
async execution to pass additional parameters such as the ID of the
request so as to tie up the async response or the object on which the
async method has to be executed or anything else.

Collapse
/// <summary> 
/// This delegate describes the method on the interface to be called.
/// </summary>
/// <typeparam name="T">This is the type of the interface</typeparam>
/// <param name="proxy">This is the method.</param>
/// <param name="obj">This is any object which may be used to identify
/// execution instance.</param>
public delegate void UseServiceDelegateWithAsyncReturn<T>(T proxy, object obj);

The proxy in this case is quite similar, except that now the execution is done on a new thread to provide the async pattern.

Collapse
/// <summary> 
/// This method calls the WCF Service in a new thread. The calling of other method
/// for result is the
/// responcibility of the client code
/// </summary>
/// <param name="codeBlock">The method on the WCF service to be called</param>
/// <param name="WCFEndPoint">This is the WCF end point</param>
/// <param name="obj">This is any object which may help in exeution of the async
/// parameters</param>
public void UseAsyncWithReturnValue(UseServiceDelegateWithAsyncReturn<T> codeBlock,
string WCFEndPoint, object obj)
{
try
{
this.proxy = GetChannelFactory(WCFEndPoint).CreateChannel() as IClientChannel;
if (this.proxy != null)
{
this.codeBlockWithAsyncReturn = codeBlock;
new Thread(() =>
{
//Create a new thread and on the new thread call the methos
codeBlock((T)this.proxy,obj);
this.proxy.Close();
}).Start();
}
}
catch (CommunicationException communicationException)
{...

The client

The client is called on a separate thread, but this time, the client
code just doesn’t tell the method to be called, it also calls up an
async method to pass results: CallBackForReturnValueOfGetData.

We should note that the same object (Guid in this case)
is passed back. In certain scenarios, we may choose to send the method
as the object or any other code. It should be noted that type safety is
maintained.

Collapse
new LamdaProxyHelper<IService1>().UseAsyncWithReturnValue((proxy, obj) => 
{
//save the return value in value
value = proxy.GetData(9);
CallBackForReturnValueOfGetData(value, (Guid)obj);
}, "WCFEndPoint",Guid.NewGuid());
//use the end point name WCFEndPoint for this proxy

/// <summary>
/// This is a type safe return method for getting the return value on a
/// seperate thread. This is called when the method is actully invoked.
/// </summary>
/// <param name="returnValue">This is the return value</param>
static void CallBackForReturnValueOfGetData(string returnValue,Guid id)
{
Console.WriteLine("The return value is =" +
returnValue+" which was called on the Guid = " +id);
}

Calling async with no return value

This is a similar proxy helper code in which we exploit the BeginInvoke method on a delegate. It allows completion of the code on a separate thread and then calls the method implementing the AsyncCallback delegate.

The proxy helper

In this code, a delegate of type AsyncCallback via AsyncResults called by the method closes the proxy and then calls the callBack method.

Collapse
/// <summary> 
/// Invokes the method on the WCF interface with the given end point to
/// create a channel
/// Usage
/// new ProxyHelper<InterfaceName>().Use(serviceProxy =>
/// {
/// value = serviceProxy.MethodName(params....);
/// }, "WCFEndPoint",callBackMethodName,id);
/// </summary>
/// <param name="codeBlock">The WCF interface method of interface of type T
/// </param>
/// <param name="WCFEndPoint">The end point.</param>
/// <param name="obj">The object instance used to identify in callback</param>
public void UseAsync(UseServiceDelegate<T> codeBlock, string WCFEndPoint,
AsyncCallback callBack,object obj)
{
try
{
this.proxy = GetChannelFactory(WCFEndPoint).CreateChannel() as IClientChannel;
if (this.proxy != null)
{
this.proxy.Open();
this.callBack = callBack;
this.codeBlock = codeBlock;
IAsyncResult result = codeBlock.BeginInvoke((T)this.proxy, AsyncResult, obj);
}

The client

The client in this case is rather self explanatory, and is doing similar tasks as before.

Collapse
new LamdaProxyHelper<IService1>().UseAsync(serviceProxy => 
{
serviceProxy.GetDataUsingDataContract(compositeType);
}, "WCFEndPoint", AsyncResultCallBack, Guid.NewGuid());

GetChannelFactory

This class is a simple method whose job is to provide a channel
based on the channel name. It creates a singleton for each channel and
keeps them. This gives performance benefits as creation of a channel is
expensive.

Collapse
/// </summary> 
/// <param name="WCFEndPoint">This is the end point</param>
/// <returns>Return List of all the invoked proxies</returns>
private ChannelFactory<T> GetChannelFactory(string WCFEndPoint)
{
ChannelFactory<T> channelFactory = null;
//Check if the channel factory exists
//Create and return an instance of the channel
if (! channelPool.TryGetValue(WCFEndPoint,out channelFactory))
{
channelFactory = new ChannelFactory<T>(WCFEndPoint);
channelPool.Add(WCFEndPoint, channelFactory);
}
return channelFactory;
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

gaurav_verma_mca


Architec

This entry was posted in SOA. Bookmark the permalink.

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s