这个问题是围绕如何构建 WCF 服务以使其易于随着时间的推移而发展的。如果不描述问题,很难得到对此的深度回应。
背景
我正在开发一个大型的 WCF 服务和客户端系统。服务器端“易于”更新,因为只有 10 台服务器在运行此代码。
尽管自动化程度很高,但客户端很难更新,在 300,000+ WCF 客户端中,更新总是需要时间,并且只能在两到三周的时间内达到很高的更新成功率。
数据合同
[DataContract]
public class MyContract
{
[DataMember]
public int Identity {get; set;}
[DataMember]
public string Name {get; set;}
// More members
}
DataContract
很难初始化,并且有一个标准类MyContractFactory
来初始化为您的机器获取适当的实例。
public class static MyContractFactory
{
public static MyContract GetMyContract()
{
// Complex implementation
}
}
服务合同
这DataContract
在一系列 Web 服务中非常常见。
namespace MyPrefix.WebServicve1
{
[ServiceContract]
public class IMyInterface1
{
[OperationContract]
public void DoSomethingWithMyContract(MyContract data);
}
[ServiceContract]
public class IMyInterface2
{
[OperationContract]
public void DoSomethingDifferentWithMyContract(MyContract data);
}
}
客户
我的客户端是基于插件的,插件在单独的进程或应用程序域中运行,具体取决于我们对该插件的信任级别。
实施1
我对这个(默认 WCF)的初始实现最终DataContract
在一个程序集中ServiceContract
, 和在它自己的程序集中实现。
客户最终得到了一个非常丑陋的,
MyWebService1.MyContract
MyWebService2.MyContract
MyContractFactory
几乎每个插件都有一个复制和粘贴。虽然DataContract
是相同的,但客户端不包含DataContract
程序集的事实意味着它作为不同的对象出现在不同的命名空间下。
实施 2
客户端现在包含DataContract
程序集,ServiceContracts
在服务实现的单独程序集中,ServiceContract
如果它有助于代码重用(不再复制和粘贴),客户端可以包含一些程序集。
问题
在第二个实现中,我现在面临的困难是,如何更新我的DataContract
and ServiceContracts
?
我是否更新相同的程序集并增加版本号?在所有客户端升级时如何保持向后兼容性?在客户端更新之前中断客户端是不可接受的。
我是否创建一个新的程序集,其类扩展
MyDataContract
、新方法接受新类型下的新类型ServiceContract
?这是否意味着对于我的合同的每一次细微更改,我都需要一个新的组件?我将如何阻止它在几年内达到数百个?其他一些解决方案?
不管我考虑了哪些解决方案,它们似乎都有一个主要的缺点。
似乎没有(至少对我而言),
- 在客户端更新之前保持向后兼容性
- 随着软件随着时间的推移而不断发展,让客户保持整洁而不臃肿
- 没有严重污染我的(需要一个新的“名字”的
ServiceContract
重载)。OperationContract
我已经有类似下面的东西了,随着时间的推移,它让我觉得是一场噩梦。
操作合同复杂性
[OperationContract]
public void DoSomethingWithMyContract(MyContract data);
[OperationContract(Name = "DoSomethingWithMyDataByAdditionalData"]
public void DoSomethingWithMyContract(MyContract data, MyContract2 additionalData);
我正在寻找一种在大规模环境中工作了一段时间的解决方案。博客条目等非常受欢迎。
更新 1
通过使用“无模式”更改的限制,不同的命名空间似乎是唯一确定的方法。然而,它并不像预期的那样工作,例如下面
[ServiceContract(
Name = "IServiceContract",
Namespace = "http://myurl/2012/05")]
public interface IServiceContract1
{
// Some operations
}
[ServiceContract(
Name = "IServiceContract",
Namespace = "http://myurl/2012/06")]
public interface IServiceContract2
{
// Some different operations using new DataContracts
}
通过以下服务
public class MyService : IServiceContract1, IServiceContract2
{
// Implement both operations
}
和以下配置
<service behaviorConfiguration="WcfServiceTests.ServiceBehavior"
name="Test.MyService">
<endpoint
address="2012/05"
binding="wsHttpBinding"
contract="Test.IServiceContract1">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint
address="2012/06"
binding="wsHttpBinding"
contract="Test.IServiceContract2">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
结果是两个具有两个不同名称的合同,我希望我可以指出我的客户,
旧版本http://myurl.com/MyService.svc/2012/05 和http://myurl.com/MyService.svc/2012/06
,但似乎如果我想保留 ServiceContract 名称,它们必须是两个单独的服务,而不是同一服务的单独端点地址?
更新 2
我最终使用了我在update 1中描述的方法。虽然 WSDL 看起来有问题,但当我对此进行测试时,该服务在旧客户端下确实是向后兼容的。