另一种方法是实现服务合同的版本控制:此时您可以利用 WCF 自己的功能来忽略不会破坏客户端的微小更改,如此版本控制策略页面中所列。
在图 1 中,您可以看到在向操作签名中添加新参数、从操作签名中删除参数以及添加新操作时,客户端不受影响。
如果仍然存在重大更改或您的客户端必须支持这两个版本(如果我错了,请纠正我,因为我不知道您的部署策略),您可以在不同的端点上提供不同版本的服务并拥有 WCF客户端代码中的客户端工厂,然后可以将其配置为为适当的端点返回客户端。
至此,您已经隔离了不同客户端中的不同实现,这可能比现在更干净,也更少维护噩梦。
非常基本的示例实现来解决问题:假设我们的服务有两个不同的合同,一个旧合同和一个新合同。
[ServiceContract(Name = "Service", Namespace = "http://stackoverflow.com/2012/03")]
public interface IServiceOld
{
[OperationContract]
void DoWork();
}
[ServiceContract(Name = "Service", Namespace = "http://stackoverflow.com/2012/04")]
public interface IServiceNew
{
[OperationContract]
void DoWork();
[OperationContract]
void DoAdditionalWork();
}
请注意这两个服务如何具有相同的名称但不同的命名空间。
让我们处理一个客户端必须能够同时支持扩展服务和新服务以及旧服务的问题。假设我们想在之前调用 DoWork 时调用 DoAdditionalWork 方法,并且我们想在客户端处理这种情况,因为假设 DoAdditionalWork 可能需要来自客户端的一些额外参数。那么服务的配置可能是这样的:
<service name="ConsoleApplication1.Service">
<endpoint address="http://localhost:8732/test/new" binding="wsHttpBinding" contract="ConsoleApplication1.IServiceNew" />
<endpoint address="http://localhost:8732/test/old" binding="wsHttpBinding" contract="ConsoleApplication1.IServiceOld" />
...
</service>
好的,我们有服务端,现在是有趣的部分:我们希望使用相同的接口与服务进行通信。在这种情况下,我将使用旧的,但您可能需要在两者之间放置一个适配器。理想情况下,在我们的客户端代码中,我们会这样做:
IServiceOld client = *Magic*
client.DoWork();
在这种情况下,魔术是这样一个简单的工厂:
internal class ClientFactory
{
public IServiceOld GetClient()
{
string service = ConfigurationManager.AppSettings["Service"];
if(service == "Old")
return new ClientOld();
else if(service == "New")
return new ClientNew();
throw new NotImplementedException();
}
}
我将使用哪个客户端的决定委托给 app.config,但您可以在此处插入版本检查。ClientOld 的实现只是 IServiceOld 的常规 WCF 客户端:
public class ClientOld : IServiceOld
{
private IServiceOld m_Client;
public ClientOld()
{
var factory = new ChannelFactory<IServiceOld>(new WSHttpBinding(), "http://localhost:8732/test/old");
m_Client = factory.CreateChannel();
}
public void DoWork()
{
m_Client.DoWork();
}
...
}
ClientNew 实现了我们希望的行为,即调用 DoAdditionalWork 操作:
public class ClientNew : IServiceOld
{
private IServiceNew m_Client;
public ClientNew()
{
var factory = new ChannelFactory<IServiceNew>(new WSHttpBinding(), "http://localhost:8732/test/new");
m_Client = factory.CreateChannel();
}
public void DoWork()
{
m_Client.DoWork();
m_Client.DoAdditionalWork();
}
...
}
就是这样,现在我们的客户端可以像下面的例子一样使用:
var client = new ClientFactory().GetClient();
client.DoWork();
我们取得了什么成就?使用客户端的代码是从实际的 WCF 客户端必须做的额外工作中抽象出来的,并且关于使用哪个客户端的决定委托给工厂。我希望这个样本的一些变化/扩展能够满足您的需求。