Introduction
In my opinion, Microsoft failed with the implementation of the generic ClientBase implementation that is used for connecting with services. The channel contained within the client base implementation will become invalid when a problem occurs during the communication with the service.
The downside is that this channel cannot be revived so that it can be reused. The only solution is to dispose the current instance and to recreate a new one. This will put a strain on the developers since they need to implement the code for handling the invalid channel and for recreating the actual client proxy.
As one can imagine, this is not efficient and can cause problems since each client proxy needs to be implemented in their own way. Some functionality can be copied and pasted, but this will be error prone.
Other Solutions
I did some browsing to see if others ran into the same problems. The only interesting solution I found was at http://www.acorns.com.au/blog/?p=113 and shows a dynamic proxy which is generated at runtime. This was almost exactly what I had been looking for. The solution however was too complex for my taste, so I started on my own version while reusing certain ideas and the intermediate language generation.
The solution to the problem is to encapsulate the actual client proxy, which is based on the ClientBase, in a specific class which will contain the functionality to recreate the proxy in case of problems. The problem however is that the calls present on the service interface need to be extended with a way to detect these problems. To be able to handle this in a generic way, we need to generate this functionality at runtime to prevent that we still need actions to keep the implementation up to date. This can be done by creating a temporary assembly which will contain the implementation for the specific implementation of the interface by generating intermediate language.
So a generic ProxyClassBuilder
is available which will generate a type that contains the methods which will handle the calls to the service implementation. Each method will contain functionality for catching exceptions and for handling them accordingly.
Code Generation
I will explain shortly what happens when the following service contract is being used:
[ServiceContract]
public interface ITestService
{
[OperationContract]
void DummyCall();
}
The following will trigger the creation of the specific client proxy which includes the method and the implementation that will handle any exceptions that occur.
clientProxy = ClientProxy<itestservice />.GetInstance("identifier", binding, endpointAddress);
The client proxy that is returned is derived from the ClientProxy
class and will contain the following implementation:
public class TestService : ClientProxy<itestservice />, ITestService
{
public TestService(Binding binding1, EndpointAddress address1) :
base(binding1, address1)
{
}
public void DummyCall()
{
try
{
base.Channel.DummyCall();
}
catch (Exception exception)
{
this.HandleException(exception);
}
}
}
It will pass the method call onto the enclosed channel which will eventually call the service. Any exceptions are caught and passed onto the HandleException
method which resides in the base class.
Classes Involved
The generic ClientProxy
is the entry point and the one that is to be used by the consumer of the service. It has an abstract
method GetInstance
which is used to get an instance of the generated class. This generated class is completely built by the ProxyClassBuilder
. For performance issues, the ClientProxy
will contain a cache with already generated client proxies. This ensures that the communication with a certain service within the same client is done via one instance of the proxy.
The generic ProxyClassBuilder
is responsible for the generation of a temporary type. This type is cached internally to prevent that it needs to be generated more than once.
The generic InternalClientProxy
is the actual proxy that talks with the service. This one is recreated when the connection with the server is somehow severed.
The Enclosed Solution
The enclosed solution contains 3 projects:
- The test application with a simple user interface which supports that starting/stopping of the service, calling the methods on the client proxy and logging some information regarding the process.
- The test service itself, it contains both the interface and the implementation. This is not the way to go, but hey it is just an example.
- The actual implementation for the dynamic client proxy. This contains the interesting part with the generation of the client proxy by using the intermediate language.
History
- 8 Nov 2010 - First version