这个问题围绕如何构建WCF服务以使其随着时间的推移变得容易.在没有描述问题的情况下很难得到对此的响应深度.
背景
我正在开发一个大型的WCF服务和客户端系统.服务器端"易于"更新,因为只有10个服务器运行此代码.
客户很难更新,尽管自动化程度很高,但在300,000多个WCF客户端,更新总是需要时间,并且只能在两到三周内实现高更新成功率.
数据合同
[DataContract]
public class MyContract
{
[DataMember]
public int Identity {get; set;}
[DataMember]
public string Name {get; set;}
// More members
}
Run Code Online (Sandbox Code Playgroud)
在DataContract难以初始化并有一个标准的MyContractFactory类初始化获得相应的实例为您的机器.
public class static MyContractFactory
{
public static MyContract GetMyContract()
{
// Complex implementation
}
}
Run Code Online (Sandbox Code Playgroud)
ServiceContracts
这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);
}
}
Run Code Online (Sandbox Code Playgroud)
客户
我的客户端是插件,基于在单独的进程或应用程序域中运行的插件,具体取决于我们在该插件中的信任级别.
实施1
我对此(默认WCF)的初始实现最终得到了DataContract一个程序集ServiceContract,并在其自己的程序集中实现.
客户最终非常难看,
MyWebService1.MyContract
MyWebService2.MyContract
Run Code Online (Sandbox Code Playgroud)
随着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);
Run Code Online (Sandbox Code Playgroud)
我正在寻找一个在大规模环境中工作了一段时间的解决方案.博客条目等非常受欢迎.
更新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
}
Run Code Online (Sandbox Code Playgroud)
有以下服务
public class MyService : IServiceContract1, IServiceContract2
{
// Implement both operations
}
Run Code Online (Sandbox Code Playgroud)
和以下配置
<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>
Run Code Online (Sandbox Code Playgroud)
结果有两个不同名称的合同,我希望我能指出我的客户,
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"]
Run Code Online (Sandbox Code Playgroud)
或类似的东西 - 一些人喜欢按年/月的版本:
[ServiceContract(Namespace="http://services.yourcompany.com/Service1/2012/05"]
Run Code Online (Sandbox Code Playgroud)
这允许您拥有相同服务的多个版本,并且只要客户端使用旧版本(由服务命名空间指示)调用,它们就会获得旧版本(只要您仍然公开它).
看到: