我有一个服务结构有状态服务,它公开如下所示的接口:
public interface IAction
{
Task GetCustomer (Customer customer)
} 客户类看起来就像
[DataContract]
public class Customer
{
[DataMember]
public string Id {get;set;}
[DataMember]
public string Name {get;set;}
}我现在已经通过nuget与服务的客户端共享了包含上述模型和接口的程序集。
一段时间后,我需要更新其他客户端的Customer类,因此我通过添加额外的可空属性执行以下操作
[DataContract]
public class Customer
{
[DataMember]
public string Id {get;set;}
[DataMember]
public string Name {get;set;}
[DataMember]
public ulong? Salary {get;set;}
}因为我已经添加了一个可空的数据成员,所以我假设我只需要与较新的客户端共享这个新模型和契约,并且第一个客户端不必费心地进行更新。
但是,我注意到有以下例外情况:
{"Interface id 'xxxxxxxx' is not implemented by object '**'"}在阅读了多个SO答案(这里、这里)之后,我得出的结论是,客户端始终必须具有当前运行的服务版本中的接口和模型的准确引用。
这是一个相当大的限制,因为我不应该被迫更新所有客户端。添加的额外可选参数不应强制更新旧客户端,特别是如果服务能够保证完全的向后兼容性。
是否有一种方法可以解决这样的问题:以一种向后兼容的方式更新服务接口,而不必更新旧的客户端?
发布于 2018-02-25 23:40:03
这是一个相当大的限制,因为我不应该被迫更新所有客户端。添加的额外可选参数不应强制更新旧客户端,特别是如果服务能够保证完全的向后兼容性。
这根本不是一个限制,因为一个、服务不应该知道客户端和两个,操作/成员的添加不被认为是破坏的,因为客户端不需要知道添加。
服务中的合同更改被认为是“不破坏”。与您共享的其他类似问题的链接没有完全解决您的问题,这些问题可以分为以下几个要点:
1&2是关于--客户端正确地发现了服务,使用您的服务的客户端需要知道它是否具有松散的版本兼容性--您需要在调用服务之前确认客户端没有对旧服务执行任何模式验证。如果是这样的话,那么您需要使用显式的XML名称空间,并定义新的契约和新的服务定义。
这是,不是一个限制,而是严格的版本控制,而且它也是关于以前与您的服务绑定的客户端未知的数据类型,这些数据类型由于回调而出现异常,这是合理的,您应该接受它与SOA无关。
要使用此解决方案,您可能需要以下列方式定义您的合同和服务:
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 {...}
} 新添加的成员的另一个版本如下,
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提供回调方法,以便在传入流中不存在成员时提供合理的缺省值。
[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");
}
}发布于 2018-02-26 18:21:02
要使用相同的远程处理服务支持多个客户端,您需要两个不同的定义良好的契约/接口。这是按照远程服务织物的设计。
通常,您应该只允许对fabric集群中的服务通信进行远程处理。原因是因为
A - DataContractSerializer与服务结构紧密耦合.我假设您的客户不知道或需要知道您所使用的服务托管框架。
B -如果您要公开远程处理接口,它将限制客户端访问端口(在domian/网络中)。
我对实现版本控制的建议是为您的服务公开定义良好的Web 'JSON/REST‘端点。在这里,您可以很容易地区分V1和V2,同时也可以使用单一的“Customer”类。
如果出于某种原因,您仍然希望将远程处理与版本控制一起用于您的服务。您应该看到将您的服务部署在一个独占流程中,该流程允许部署同一服务的多个版本。这个部署模型在两种情况下都很有用。
从以下链接了解有关应用程序部署的更多信息
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
https://stackoverflow.com/questions/48314135
复制相似问题