首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >向后兼容服务接口,而不更新蔚蓝服务结构中的老客户端。

向后兼容服务接口,而不更新蔚蓝服务结构中的老客户端。
EN

Stack Overflow用户
提问于 2018-01-18 05:01:41
回答 3查看 480关注 0票数 8

我有一个服务结构有状态服务,它公开如下所示的接口:

代码语言:javascript
复制
public interface IAction 
{
   Task GetCustomer (Customer customer)
} 

客户类看起来就像

代码语言:javascript
复制
[DataContract]
public class Customer
{
   [DataMember]
   public string Id {get;set;}

   [DataMember]
   public string Name {get;set;}
}

我现在已经通过nuget与服务的客户端共享了包含上述模型和接口的程序集。

一段时间后,我需要更新其他客户端的Customer类,因此我通过添加额外的可空属性执行以下操作

代码语言:javascript
复制
[DataContract]
public class Customer
{
   [DataMember]
   public string Id {get;set;}

   [DataMember]
   public string Name {get;set;}

   [DataMember]
   public ulong? Salary {get;set;}
}

因为我已经添加了一个可空的数据成员,所以我假设我只需要与较新的客户端共享这个新模型和契约,并且第一个客户端不必费心地进行更新。

但是,我注意到有以下例外情况:

代码语言:javascript
复制
{"Interface id 'xxxxxxxx' is not implemented by object '**'"}

在阅读了多个SO答案(这里这里)之后,我得出的结论是,客户端始终必须具有当前运行的服务版本中的接口和模型的准确引用。

这是一个相当大的限制,因为我不应该被迫更新所有客户端。添加的额外可选参数不应强制更新旧客户端,特别是如果服务能够保证完全的向后兼容性。

是否有一种方法可以解决这样的问题:以一种向后兼容的方式更新服务接口,而不必更新旧的客户端?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-02-25 23:40:03

这是一个相当大的限制,因为我不应该被迫更新所有客户端。添加的额外可选参数不应强制更新旧客户端,特别是如果服务能够保证完全的向后兼容性。

这根本不是一个限制,因为一个、服务不应该知道客户端和两个,操作/成员的添加不被认为是破坏的,因为客户端不需要知道添加。

服务中的合同更改被认为是“不破坏”。与您共享的其他类似问题的链接没有完全解决您的问题,这些问题可以分为以下几个要点:

  1. 服务发现
  2. 服务版本化
  3. 实现IExtensibleDataObject (如果问题是由服务新添加的往返后的数据/数据类型造成的)

1&2是关于--客户端正确地发现了服务,使用您的服务的客户端需要知道它是否具有松散的版本兼容性--您需要在调用服务之前确认客户端没有对旧服务执行任何模式验证。如果是这样的话,那么您需要使用显式的XML名称空间,并定义新的契约和新的服务定义。

这是,不是一个限制,而是严格的版本控制,而且它也是关于以前与您的服务绑定的客户端未知的数据类型,这些数据类型由于回调而出现异常,这是合理的,您应该接受它与SOA无关。

要使用此解决方案,您可能需要以下列方式定义您的合同和服务:

代码语言:javascript
复制
public interface IPurchaseOrderV1  
{  
    string OrderId { get; set; }  
    string CustomerId { get; set; }  
}  

[DataContract(  
Name = "PurchaseOrder",  
Namespace = "http://examples.microsoft.com/WCF/2005/10/PurchaseOrder")]  
public class PurchaseOrderV1 : IPurchaseOrderV1  
{  
    [DataMember(...)]  
    public string OrderId {...}  
    [DataMember(...)]  
    public string CustomerId {...}  
}  

新添加的成员的另一个版本如下,

代码语言:javascript
复制
public interface IPurchaseOrderV2  
{  
    DateTime OrderDate { get; set; }  
}

[DataContract(   
Name = "PurchaseOrder",  
Namespace = "http://examples.microsoft.com/WCF/2006/02/PurchaseOrder")]  
public class PurchaseOrderV2 : IPurchaseOrderV1, IPurchaseOrderV2  
{  
    [DataMember(...)]  
    public string OrderId {...}  
    [DataMember(...)]  
    public string CustomerId {...}  
    [DataMember(...)]  
    public DateTime OrderDate { ... }  
}  

对于这段代码的源代码,您可以参考这个链接,它肯定会帮助您理解服务的什么问题以及如何修改它。

只是将它添加到isRequired属性中,作为一个后置,DataMember默认为false。

下面的内容来自这里

如果成员的默认值为null或零是不可接受的,则应该使用OnDeserializingAttribute提供回调方法,以便在传入流中不存在成员时提供合理的缺省值。

代码语言:javascript
复制
[OnDeserialized]
public void OnDeserialized(StreamingContext context)
{
    if (this.id == null) throw new ArgumentNullException("id");
    if (this.Name == null) throw new ArgumentOutOfRangeException("name");
    if (this.Salary < 0) throw new ArgumentOutOfRangeException("salary");

    if (this.Salary > 0)
    {
        throw new InvalidOperationException("No child labor allowed");
    }
}
票数 6
EN

Stack Overflow用户

发布于 2018-02-26 18:21:02

要使用相同的远程处理服务支持多个客户端,您需要两个不同的定义良好的契约/接口。这是按照远程服务织物的设计。

通常,您应该只允许对fabric集群中的服务通信进行远程处理。原因是因为

A - DataContractSerializer与服务结构紧密耦合.我假设您的客户不知道或需要知道您所使用的服务托管框架。

B -如果您要公开远程处理接口,它将限制客户端访问端口(在domian/网络中)。

我对实现版本控制的建议是为您的服务公开定义良好的Web 'JSON/REST‘端点。在这里,您可以很容易地区分V1和V2,同时也可以使用单一的“Customer”类。

  • 版本V1将只使用Id和名称说明该方法
  • 版本V2将与任何新添加的数据成员一起公开该方法。

如果出于某种原因,您仍然希望将远程处理与版本控制一起用于您的服务。您应该看到将您的服务部署在一个独占流程中,该流程允许部署同一服务的多个版本。这个部署模型在两种情况下都很有用。

  • 您希望维护应用程序的多个版本。
  • 您希望支持单个集群中的多个客户端的独立应用程序(以节省成本)。

从以下链接了解有关应用程序部署的更多信息

https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-hosting-model

https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-concepts-scalability

票数 4
EN

Stack Overflow用户

发布于 2018-02-20 09:15:59

这是远程处理的一个限制,调用方和发送方必须清楚地理解相同的消息(版本)才能进行通信,如果一个人不理解对方,则会抛出此异常。

如果您必须具有这种灵活性,则可以:

  • 尝试使用远程处理V2并实现自定义序列化。看这里
  • 或者,使用不同的方法来实现您自己的ServiceInstanceListener来通信和序列化消息。你可以使用http通讯套接字。看这里
  • 或者,您可以为新版本的消息创建不同的契约,并在使用它时解析新版本。(如果其他两个不符合你的要求,那是个建议)。
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48314135

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档