zoukankan      html  css  js  c++  java
  • WCF中的变更处理

    详解WCF中的变更处理:不可不知的最佳实践

    2012/1/6 13:08:37 | 阅读10
    本文出自:

    http://www.haogongju.net/art/1210410 可参考http://msdn.microsoft.com/zh-cn/library/ms731060.aspx

    【51CTO快译】变更总是存在的,包括需求变更、环境变更和过程变更。这些因素加在一起使你的WCF服务也会发生变更,幸运的是,可以在设计之初就采取一些方法来尽量避免这些变更,或者说减少变更给用户和自己带来的影响。

    本文探讨的不仅仅是前期如何做才能减少变更次数,同时还讨论了在遇到未曾预见的大型变更前该如何应对。

    51CTO编辑推荐:WCF开发基础专题

    确定变更

    在开始着手处理变更之前,有必要弄清楚在基于WCF的服务中发生变更意味着什么,下面的行为构成了变更:

    1、数据契约

    (1)增加一个数据成员

    (2)移除一个数据成员

    (3)重命名一个数据成员

    (4)改变数据成员的类型

    2、服务契约

    (1)增加一个操作

    (2)移除一个操作

    (3)重命名服务契约

    3、操作契约

    (1)重命名一个操作

    (2)修改操作的签名

    这些变更可能源于新的业务需求、硬件整合、业务兼并、新条例或任何其它外部因素,底线是当某些东西超出了开发人员的控制变更外,软件就必须要调整,在WCF世界中处理变更总是有好消息也有坏消息,因为有时候处理起来很简单,但有时候会让你惧怕,但却不得不响应。

    WCF中的版本和变更控制

    在.Net世界中,处理变更时第一个要考虑的就是如何控制版本,通过版本组装,可以在后续的组件版本中允许无法预料的或有问题的变更,使用这种方式,受影响的客户端可以继续使用旧版本,你就可以避免因变更引起的头痛问题。

    那么WCF支持版本控制吗?答案有点担忧。当你在WCF中创建一个数据契约时,这个契约会生成一个XML schema,引用这个schema的用户使用它生成一个代理类,严格地说,数据没有经过这个schema验证,正如你将看到的,这将对服务使用者产生一些异常或令人沮丧的行为。

    在进入细节前,仔细研究下面例子自己先熟悉一下,它提供了本文剩余部分讨论的基础:

    namespace SampleService

    {

    [ServiceContract]

    public interface IPersonService

    {

    [OperationContract]

    Person GetPerson(int personId);

    [OperationContract]

    void UpdatePerson(Person p);

    }

    public class Person

    {

    private string _firstName = string.Empty;

    private string _lastName = string.Empty;

    [DataMember]

    public string FirstName

    {

    get { return _firstName; }

    set { _firstName = value; }

    }

    [DataMember]

    public string LastName

    {

    get { return _lastName; }

    set { _lastName = value; }

    }

    }

     

    数据契约变更

    Person DataContract定义了两个属性:FirstName和LastName,如果某个客户的引用了这个服务,你接着将LastName改为SurName,客户的不会被真正断开,只不过在客户端的代理类中,LastName属性将会显示为空,这时因为当客户的将消息持久化到Person类时,发现没有任何名叫LastName的元素了。

    这个简单的变更不会让客户端出现异常错误,但糟糕的是会导致一个异常行为,除非你亲自了解每个客户的应用程序使用的web服务,修改将会是灾难性的,作为一名开发人员,你应该尽一切努力来保护变更给客户带来的影响。

    最初,你可以先应用一些最佳实践,帮助那些孤立的客户端应对变更,一个数据契约的升级版本看起来如:

    [DataContract(Namespace="http://types.mycompany.com/2009/05/25", Name="PersonContract")]

    public class Person : IExtensibleDataObject

    {

    private string _firstName = string.Empty;

    private string _lastName = string.Empty;

    private ExtensionDataObject _extensionData;

    [DataMember(Name="FirstName")]

    public string FirstName

    {

    get { return _firstName; }

    set { _firstName = value; }

    }

    [DataMember(Name="LastName")]

    public string LastName

    {

    get { return _lastName; }

    set { _lastName = value; }

    }

    public ExtensionDataObject ExtensionData

    {

    get { return _extensionData; }

    set { _extensionData = value; }

    }

     

    在DataContract和DataMember属性上增加了Namespace、Name和Order参数来控制DataContractSerializer的行为,引用这些服务时会增加一个客户端代理,Name参数会导致串行转换器使用标示的值,而不是真实的公共成员或属性的名字,这种方法允许在内部实现变更,不影响客户端,如下面的变更:

    [DataMember(Name="LastName")]

    public string SurName

    {

    get { return _lastName; }

    set { _lastName = value; }

     

    属性名从LastName变成SurName将会中断现有的客户端,因为客户端使用的Name参数任然是LastName,仅仅内部实现变更了。

    第二个显而易见的变更是增加了IExtensibleDataObject接口,实现这个接口让未在契约中明确定义的客户端保留数据,这看起来没什么作用,但是当客户端希望在同一个Person对象上执行处理并返回时就有用了,客户端可以保留新的数据项。例如,使用下面的新成员更新PersonContract不会强制现有的客户端也跟着一起更新:

    [DataMember(Name = "MiddleName", Order = 3)]

    public string SurName

    {

    get { return _middleName; }

    set { _middleName = value; }

     

    事实上,这个成员将允许现有的客户端保留一个值放于MiddleName,实现IExtensibleDataObject对于未来你的数据契约是一个好方法,作为一个最佳实践,你应该在所有数据契约中使用它。

    请记住,客户端实际上可以选择一个外部schema验证消息,因此,你在处理数据契约变更时有两件事需要考虑:有schema验证和无schema验证。

    当客户端添加了schema验证后,数据契约中任何添加、修改或减去数据项的行为都将导致验证失败,因此,在实际生活照,试验了任何严格的schema验证后,契约就不应该改变了,相反,你应该创建一个全新的契约并在契约中使用不同的命名空间,以表明是新版本。

    例如,从实现的视角来看,你应该需要两个独立的服务点来使这两个版本可用:

    Original Version: [DataContract(Namespace="http://schemas.mycompany.com/2009/05/25")]

    New Version: [DataContract(Namespace="http://schemas.mycompany.com/2009/06/18")] 

     

    幸运的是,严格的schema验证不是默认行为,这意味着你在不中断客户端的情况下可以添加或移除数据成员,然而,根据前面讨论过的异常行为,移除一个数据成员不是个好主意,换句话说,增加一个数据成员容易,用户会忽略他们不知道的额外成员。

    最关键的是使用DataMember属性的Order参数,使用这个参数告诉串行转化器在XML中各个成员应该显示成怎么样,一个非预期的变更可能会导致XML与原始schema不一致,从一开始就使用Order参数可以避免这个问题,如果你不使用Order参数,串行转化器将按照下面的顺序执行:

    1、来自基础类型的成员

    2、无Order参数的成员(按字母顺序)

    3、有Order参数的成员(按值的顺序)

    数据契约变更的最后一种情况是修改数据成员的类型,在这种情况下,最佳的做法是和新的服务契约、实现和终结点一道创建一个新版本的数据契约。

    服务契约变更

    再说一次,所有服务契约应该按照最佳实践,在ServiceContract属性上同时使用Name和Namespace参数,Person服务契约的一个更新版本看起来如:

    [ServiceContract(Name="PersonService", Namespace="http://services.mycompany.com/2009/05/25"]

    public interface IPersonService 

     

    和数据契约一样,使用Name隔离服务用户和真实接口名,允许内部实现按需变更,Namespace允许你在将来对契约进行版本控制,记住新版本也需要新的终点。

    可以在不中断现有用户的情况下往服务契约中添加操作,用户会忽略新增加的操作。另一方面,移除操作将会中断现有用户,如同所有的中断变更,移除操作需要一个新版本和一个新的终点。

    操作契约变更

    与服务契约和数据契约一样,应该在OperationContract属性上使用Name参数:

    [OperationContract(Name="GetPerson"]

    Person GetPerson(int personId); 

     

    再说一次,在内部实现中用户和变更是隔离的。

    最后一个需要考虑的变更是操作契约的签名,这是一个中断变更,有两种解决方案:创建一个新版本或在服务契约上添加一个新操作。

    遵守你的承诺

    变更是不可避免的,但要做好规划,并遵循一些原则,可以讲WCF服务上变更的影响降到最低,记住,当你发布一个服务时,你应该向用户提供一个承诺,让他们保证遵守契约,在现有的契约上做改动不是一件好事。

    为此,请记住下面这些最佳实践:

    1、在所有契约上使用Name和Namespace参数;

    2、在数据成员上总是使用Order参数;

    3、在所有数据契约上实现IExtensibleDataObject;

    4、为契约版本控制使用命名空间;

    5、记住所有新版本都需要新的终点;

    6、使用严格的schema验证时,不要修改契约,创建一个新版本;

    7、从服务契约中移除一个操作时,请创建一个新版本;

    8、改变一个操作的签名时,请创建一个新版本。

    记住这些最佳实践后,在处理你自身或服务用户提出的变更时就会游刃有余了。

  • 相关阅读:
    dotnet 读 WPF 源代码笔记 了解 WPF 已知问题 用户设备上不存在 Arial 字体将导致应用闪退
    WPF 基础 2D 图形学知识 判断点是否在线段上
    WPF 实现自定义的笔迹橡皮擦
    WPF 加载诡异的字体无法布局
    dotnet 5 从 IL 层面分析协变返回类型新特性
    WPF 笔迹触摸点收集工具
    dotnet C# 序列化 XML 时进行自动格式化
    自定义systemctl服务
    maven之三大特性属性、profile、资源过滤
    dependencyManagement与dependencies
  • 原文地址:https://www.cnblogs.com/mmnyjq/p/2342290.html
Copyright © 2011-2022 走看看