WCF体系结构和演进,版本
本文关键字:版本 体系结构 WCF | 更新日期: 2023-09-27 18:35:48
这个问题是关于如何构建WCF服务,使其易于随时间发展。如果不描述问题,就很难得到对此的深入回应。
背景
我正在开发一个WCF服务和客户端的大型系统。服务器端"很容易"更新,因为只有10台服务器在运行此代码。
客户端很难更新,尽管自动化程度很高,但在300000多个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
和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
我最终使用了更新1中描述的方法。虽然WSDL看起来是错误的,但当我测试过它时,该服务在旧客户端下确实是向后兼容的。
微软和大多数受人尊敬的WCF大师可能都会说同样的话:版本控制应该使用合约名称空间来处理。
我指的不是程序集的.NET命名空间——我指的是实际的WCF服务(和数据契约(命名空间——所以你应该有:
[ServiceContract(Namespace="http://services.yourcompany.com/Service1/V01"]
或者类似的东西——有些人喜欢按年/月进行版本:
[ServiceContract(Namespace="http://services.yourcompany.com/Service1/2012/05"]
这允许您拥有同一服务的多个版本,只要客户端使用旧版本(由服务命名空间指示(进行调用,它们就会获得旧版本(只要您仍然公开该版本(。
参见:
- 最佳实践:数据合同版本控制
- 服务版本控制