In order to provide symmetric programming model for the client-side as for the server-side, WCF leverages .NET Remoting transparent proxy technique so that the service contract interface can be used seamlessly as on the server-side. The svcutil.exe tool can generate WCF client proxy code for you. If you take a closer look at the generated code, you will find out that the proxy class is a subclass of ClientBase<T>. By using ChannelFactory<T>, you can create your proxy (or called channel) directly without going through ClientBase<T>.
Creating a WCF proxy is quite a heavy-weighted operation. So sometimes, you would want to create a single proxy and let multiple threads to use it. This works quite well if you don’t want the proxy to keep specific context (such as security credential) on each call.
The best practice in this case is that: you should always open WCF client proxy explicitly before you are making any calls. Here is the sample code if you use auto-generated proxy from svcutil.exe:
MyHelloServiceClient proxy = new MyHelloServiceClient();
proxy.Open();
// Make a call with the proxy
proxy.Hello("Hello world!");
Here is the sample code if you use ChannelFactory<T> to create a proxy:
ISimpleContract proxy = factory.CreateChannel();
((IClientChannel)proxy).Open();
// Make a call with the proxy
proxy.Hello("Hello world!");
If you don’t call the “Open” method first, the proxy would be opened internally when the first call is made on the proxy. This is called auto-open.
Why? When the first message is sent through the auto-opened proxy, it will cause the proxy to be opened automatically. You can use .NET Reflector to open the method System.ServiceModel.Channels.ServiceChannel.Call and see the following code:
if (!this.explicitlyOpened)
{
this.EnsureDisplayUI();
this.EnsureOpened(rpc.TimeoutHelper.RemainingTime());
}
When you drill down into EnsureOpened, you will see that it calls CallOnceManager.CallOnce. For non-first calls, you would hit SyncWait.Wait which waits for the first request to complete. This mechanism is to ensure that all requests wait for the proxy to be opened and it also ensures the correct execution order. Thus all requests are serialized into a single execution sequence until all requests are drained out from the queue. This is not a desired behavior in most cases.
To avoid such “serializing” artifact, the best practice is to open the proxy explicitly as above. Once you get to this point, you will be able to share the same proxy object among multiple threads.