为了向WCF客户端和服务端提供对称编程模型,WCF采用.NET远程传输代理技术来帮助客户端(服务契约接口)无缝连接到服务端。svcutil.exe 可以为你生成WCF客户端代理的代码。如果你仔细看过生成的客户端代理代码,你会发现代理类是ClientBase<T>的子类。通过使用ChannelFactory<T>,你可以不直接使用ClientBase<T>来创建你的代理(或者称为信道)。
创建一个WCF代理是一个重量级操作。所以有些时候你可能想要创建一个单一代理并允许多个线程来使用它。如果你不想让代理在每次调用都保留特定上下文的话,那么这种模式会很适合。
这种情况下最好的实践是:你应该在你进行任何调用之前显式打开WCF客户端代理。这里是使用svcutil.exe生成的示例:
CalculatorClient clientProxy = new CalculatorClient(); clientProxy.Open(); // Make a call with the proxy clientProxy.Add(1, 2);
这里是使用ChannelFactory<T>创建一个代理的示例:
ChannelFactory<ICalculator> channel = new ChannelFactory<ICalculator>(); ICalculator clientProxy = channel.CreateChannel(); ((IClientChannel)clientProxy).Open(); // Make a call with the proxy clientProxy.Add(1, 2);
如果你不先调用“Open”方法,那么当代理进行第一次调用的时候会在内部自动打开信道。这称作信道自动开启。
为什么?这是因为当第一条消息通过自动开启的代理时,它将会导致代理自动打开。你可以使用.NET Reflector 来打开System.ServiceModel.Channels.ServiceChannel.Call并查看以下代码:
1: if (!this.explicitlyOpened)
2: {
3: this.EnsureDisplayUI();
4: this.EnsureOpened(rpc.TimeoutHelper.RemainingTime());
5: }
当你进入EnsureOpened函数查看时,你将会看到它调用CallOnceManager.CallOnce. 对非首次调用来说,你将触发等待第一次请求完成的SyncWait.Wait方法。这个架构会保证所有的请求都等待代理打开并保证正确的执行顺序。因此所有的请求被序列化为一个单一的执行顺序直到它们被从队列中移出。这不是大多数情况下期待的行为。
为了避免诸如“序列化”操作,最好的方法是像上面代码那样显式打开信道。一旦你明白了这一点,你将能够在多个线程之间共享同一个代理对象。