http://www.baike.com/wiki/%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6
版本控制是一种记录若干文件内容变化,以便将来查阅特定版本修订情况的系统。
版本控制(Revision control)是一种软体工程技巧,籍以在开发的过程中,确保由不同人所编辑的同一档案都得到更新。
版本控制透过文档控制(documentation control)记录程序各个模组的改动,并为每次改动编上序号。这种方法是工程图维护(maintenance of engineering drawings)的标准做法, 它伴随着工程图从图的诞生一直到图的定型。 一种简单的版本控制形式,例如,赋给图的初版一个版本等级“A”。当做了第一次改变后,版本等级改为“B”,以此类推。不仅存在着保存着工程图或者软件源代码的文本文件版本控制管理,而实际上,你可以对任何类型的文件进行版本控制。
版本控制系统用于维护文件的所有版本,随着时间的推移,系统逐渐产生这些版本。使用版本控制系统,人们可以返回到各个文件以前的修订版本,还可以比较任意两个版本以查看它们之间的变化。通过这种方式,版本控制可以保留一个文件修订的可检索的准确历史日志,甚至将整个项目都回退到过去某个时间点的状态。。更重要的是,版本控制系统有助于多个人(甚至位于完全不同的地理位置)通过 Internet 或 专用网将各自的更改合并到同一个源存储库,从而协同开发项目。而由此额外增加的工作量却微乎其微。
版本控制 - 本地版本控制系统
许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别。这么做唯一的好处就是简单,不过坏处却不少:有时候会混淆所在的工作目录,弄错了文件丢了数据就没了退路。
为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。
其中最流行的一种叫做 rcs,现今许多计算机系统上都还看得到它的踪影。甚至在流行的 Mac OS X 系统上安装了开发者工具包之后,也可以使用 rcs 命令。它的工作原理基本上就是保存并管理文件补丁(patch)。文件补丁是一种特定格式的文本文件,记录着对应文件修订前后的内容变化。所以,根据每次修订后的补丁,rcs 可以通过不断打补丁,计算出各个版本的文件内容。
版本控制 - 集中化的版本控制系统
接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作?于是,集中化的版本控制系统( Centralized Version Control Systems,简称 CVCS )应运而生。这类系统,诸如 CVS,Subversion 以及 perforce 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。多年以来,这已成为版本控制系统的标准做法。
这种做法带来了许多好处,特别是相较于老式的本地 VCS 来说。现在,每个人都可以一定程度上看到项目中的其他人正在做些什么。而管理员也可以轻松掌控每个开发者的权限,并且管理一个 CVCS 要远比在各个客户端上维护本地数据库轻松容易得多。
事分两面,有好有坏。这么做最显而易见的缺点是中央服务器的单点故障。若是服务器当机一小时,那么在这一小时内,谁都无法提交更新,也就无法协同工作。如果中央服务器的磁盘发生故障,并且没做过备份或者备份得不够及时的话,还会有丢失数据的风险。最坏的情况是彻底丢失整个项目的所有历史更改记录,被客户端提取出来的某些快照数据除外,但这样的话依然是个问题,你不能保证所有的数据都已经有人提取出来。本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新信息的风险。
版本控制 - 分布式版本控制系统
于是分布式版本控制系统( Distributed Version Control System,简称 DVCS )面世了。在这类系统中,诸如 Git,Mercurial,Bazaar 还有 Darcs 等,客户端并不只提取最新版本的文件快照,而是把原始的代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。因为每一次的提取操作,实际上都是一次对代码仓库的完整备份。
更进一步,许多这类系统都可以指定和若干不同的远端代码仓库进行交互。籍此,你就可以在同一个项目中,分别和不同工作小组的人相互协作。你可以根据需要设定不同的协作流程,比方说层次模型式的工作流,这在以前的集中式系统中是无法实现的。
版本控制 - 详细内容
版本控制包括两个方面:保正人人得到的是最新的版本,记录需求的历史版本。
如果有专门的需求管理商业工具可以助您一臂之力,由于我并没有条件试用所有的需求管理工具,能够向大家推荐的只有瑞理公司的RequisitePro,推荐的一个重要原因是它能够把需求和瑞理的其他工具如Rose、TeamTest等联系起来,从而实现需求链。
能够借助工具将需求自动化固然很好,不过,工具使用不当也不会提高生产效率。需求管理的工具其实用简单的Office和任一个关系型数据库就可以解决,而且根据企业自身的特点,摸索出最适合企业用的工具。
版本控制的最简单方法是在每一个公布的需求文档的版本应该包括一个修正版本的历史情况,即已做变更的内容、变更日期、变更人的姓名以及变更的原因并根据标准约定手工标记软件需求规格说明的每一次修改。
一、版本控制业务流程
利用 WebLogic Workshop 的版本控制功能,能够在不中断当前正在运行的任何流程实例的情况下对业务流程进行更改。对业务流程进行版本控制时,便是创建了业务流程的子版本,该版本与其父版本共享同一公共 URI(接口)。运行时,标记为有效的流程版本便是将由外部客户端通过公共 URI 来访问的流程。
注意:可以对业务流程进行版本控制,但无法对与该流程关联的单个控件或其他与业务流程有关的组件(如 schema 和转换)进行版本控制。对业务流程进行版本控制时,还必须对该流程的子流程进行版本控制,因为对父流程进行版本控制时,该控制对其子流程无效。
二、版本控制中的关键术语
签入文件或目录
此操作将工作目录作为新版本复制回存储库。
签出文件或目录
此操作从存储库中将文件的最新修订版本复制到工作空间。签出目录时,将签出该目录下的所有文件和子目录。
提交文件或目录
此操作与签入文件或目录相同。版本控制用户会经常说他们“已提交更改”;这表示他们对各自文件的工作副本做了更改,并将这些更改提交到存储库。
冲突
当两名开发人员对同一文件的工作副本进行更改,并将这些更改提交到存储库时,他们的工作可能会发生冲突。在这种情况下,CVS 或 Subversion 将检测冲突,并要求某个人先解决该冲突,然后再提交他们的更改。
合并
将对相同文件的不同工作副本进行的多个更改合并到源存储库中。合并是一种管理冲突的策略,它允许多名开发人员同时工作(不必对文件进行锁定),然后将他们的工作并入一个组合版本中。当对同一文件的不同行进行两组更改时,合并这两组更改很容易,而合并操作也可正常进行。但对文件的同一行或几行进行更改时,将发生冲突,这就要求有人手动编辑该文件,然后才能将这些更改成功提交到源存储库。
存储库
具有受版本控制的所有文件的完整修订历史的共享数据库。
解决
当两名开发人员试图提交发生冲突的更改,而造成文件内的冲突时,必须通过手动编辑该文件进行处理。必须有人逐行检查该文件,以接受一组更改并删除另一组更改。除非冲突解决,否则存在冲突的文件无法成功提交到源存储库中。
修订版本
对各个文件进行具体更新的编号草案。每次编辑文件并将它提交回存储库时,该文件的修订版本号将会增加。
版本
用于标识文件集 的编号方案,可在某个时间点标记并命名这些文件集。
工作空间
要在本地硬盘或 Unix 用户帐户上编辑的文件副本。在工作空间中编辑文件时,这些文件将不再与存储库同步。这就是进度!然后您需要将更改返回存储库,以便其他人可以看到这些更改。
Subversion 词汇表
APR
Subversion 置于称为 APR(Apache 可移植运行库)的可移植层上。 这意味着 Subversion 应该在任何运行 Apache httpd 的操作系统上工作: Windows、Linux、BSD 的所有 flavors、Mac OS X、Netware 以及其它操作系统。
分支
分支是指目录和文件的现有原始树的副本。 分支的生命周期是从某事物的副本开始的,并从此副本处移动,生成自己的历史。 通常创建分支以尝试新功能,同时不影响具有编译器错误和小问题的开发的主分支。
检出
检查存储库,会在本地计算机上创建所需分支的副本。 此副本包含了您指定的存储库的最新版本。
提交
文件的提交意味着已将对本地副本所做的更改更新到存储库中。 提交文件后,用户可以查看对特定文件执行“更新”后的最新版本。
冲突
有时候,当您更新存储库中的文件时,可能会遇到冲突。 当两个或多个用户更改文件中的一些相同行时,将发生冲突。
Hook
Hook 是被存储库事件触发的程序,例如创建新版本或修改无版本属性。 Hook 中保留了足够的信息,可以告知该事件是什么、正被操作的目标是什么,以及触发此事件的人员的用户名是什么。
锁定
锁定是指一种机制,在此机制下,用户请求获得修改工作副本文件的更改的专有权限。
合并
合并是指将某分支上的更改联接到此主干或同为主干的另一个分支。
存储库
Subversion 的核心为存储库。 它是一个存储和共享数据的集中式系统。 存储库以一组树和分支的形式(即目录和文件的层次结构)存储信息。 任何数量的客户端都可以连接到存储库中,并对这些文件进行读取和写入。
存储库浏览器
在某些情况下中,可能需要直接在存储库中工作而不使用工作副本。 这便是存储库浏览器的由来。它与资源管理器窗口具有同样的图标以及用于键入将显示的存储库 URL 名称的地址栏。 它还具有诸如复制、移动和删除等的命令。
存储库 URL
可以通过本地磁盘上的不同方法或通过网络协议访问存储库。 存储库位置通常指 URL。 这些 URL 使用标准的语法,其中引用要指定的服务器名称和端口号。
撤消
如果检查时决定取消对文件所做的更改,可以使用“撤消”命令跳转回先前的更改。
修订版本
每次存储库接受提交时,都将创建文件系统树的新版本,称为修订版本。 会为每个修订版本分配唯一号,此号比上一修订版本的号大。 刚创建的存储库的初始修订版本编号为零,且其中除了空的根目录外不包含任何信息。
修订图形
修订图形是主干位置的图形化表示,其中分支与标记与主干是分离的。 这与树结构非常相似,且很容易查看这类信息。
修订版本号
创建新存储库时,其生命周期从修订版本号零开始,且每次后续提交会将修订版本号增加一。 提交后,Subversion 客户端将提供新的修订版本号。 每个修订版本号都会在下方挂起一棵树,每棵树都是存储库对待每次提交的方式快照。
转换
此子命令将更新工作副本以镜像新的 URL;通常是共享工作副本中的公共祖先的 URL。 这是将工作副本移动到新分支上的 Subversion 方法。
标记
标记主要指在每个文件上置入一个标签,无论此文件的修订版本号。 这既可以在工作副本上执行,也可以在存储库自身执行;其效果相同。
更新
更新可以使工作副本与用户对存储库所做的最新更改同步。 它会取出文件的最新工作副本置于本地驱动器上。 遵照滑块规则,总是在更改文件前更新此文件。
工作副本
工作副本是指从存储库获取的文件的现有副本和已更新副本。 若要获得工作副本文件,需要执行检出。
复制 - 修改 - 合并开发周期
由于 CVS 和 Subversion 都是功能强大的工具,所以学习过程可能会让人望而却步。大量的书籍和网站提供全面的 CVS 知识库,但提供 Subversion 知识库的并不多。但是,不是非得学会了整本书才能在软件开发实践中迅速有效地使用 CVS 或 Subversion。
在与项目的整体开发周期保持一致的情况下,CVS 和 Subversion 都允许您进行自己的开发。
1、要开始项目工作之前,您需要签出源代码。您可以签出该项目的整个 CVS 或 Subversion 存储库,也可以只签出您希望处理的那些模块。
2、通过修改这些文件和创建新文件,为项目作出贡献。
周期的这一部分并不直接涉及 CVS 或 Subversion。您可在本地计算机上使用文件编辑器修改项目文件的工作副本。还可以保存并编译您编辑过的文件,以测试您所做的更改如何影响正在处理的特定项目模块,此过程不影响其他人对同一项目文件的工作。您所执行的任何操作都不影响其他项目参与者,除非您将更改合并到项目存储库中。
3、您在自己的工作空间中测试和调整您最新的更改,以确保这些工作不中断或损坏整个项目。
4、最后,将您所做的更改返回或签入项目文件的主要或“顶级”主体,将您的工作合并到最近的工作版本(在版本控制术语表中称为 head)。
提交您的更改以与其他开发人员的工作合并是 CVS 和 Subversion 最强大的功能,但此功能也使它成为最危险的方面。有时可能因为一时糊涂,无意中覆盖了他人或您自己的更改。您所提交的更改将始终在某些方面与其他人的更改相冲突。在合作开发项目中,理解??冲突是使用 CVS 或 Subversion 时两个特别关键的方面。
所有起作用的开发人员在项目生命周期中都在不断重复着这个复制 - 修改 - 合并周期。CVS 和 Subversion 使得每个人都能同时处理项目文件,掌握其他人进行的最新更改,以及测试自己的更改如何影响整个项目,这些过程都不会中断其他开发人员的周期。
三、版本控制的选择
在八月份的时候,一些读者写信要求我说明如何控制接口的版本。实作新版接口时,有些情况你只需要强化现有类别,在其它情况你则需要实作一个可能使用前版的新类别。我想要提出的是相信大部分读者都会遇到的组合情况,这里列出当您在更新 Web 服务时最常面临到的工作:
1. 新增额外的方法。新方法在概念上和现有的 Web 服务是相关连的,而且应该在相同的端点上实作。
2. 变更方法签名码。在这个实例中,输入组件的数目和类型会改变。
3. 更新数据模型。在这个实例中,资料型别会扩充且资料成员可能会改变名称。
为了准备场景,我想要规划一个简单的 Web 服务,让它能够接受所有类型的改变,这个 Web 服务代表版本 1。类别和这个 Web 服务会对应一个命名空间同步进行。所有动作都会随着这篇文章逐步发展。
版本 1
我要为稍后会作的修改提供一个起点。每一个区段都建置在这个初始 Web 服务之上,然而下面的区段却是建立在彼此的 Web 服务上。这个服务是设定用来处理讯息,以便将两个数字加起来,以及将一些基本个人资料转换成字符串。
【WebServiceAttribute(Namespace = NamespaceConsts.AYS15Oct2002)】
public class VersionOne : System.Web.Services.WebService {
【WebMethodAttribute】
public string GetDisplayValue( Person person ) {
return person.ToString();
}
【WebMethodAttribute】
public int Add( int a, int b ) {
return a + b;
}
}
命名空间字符串会储存在一个位置中,即 NamspaceConsts 类别,来减少由于打字错误产生的问题。我会大量重复使用这个命名空间值,来减少输入错误字符串的机会。
public class NamespaceConsts {
///
/// 这个内容值包含用于 XML 命名空间的字符串
/// http://msdn.microsoft.com/samples/AYS/2002/10/15/
///
public const string AYS15Oct2002 =
"http://msdn.microsoft.com/samples/AYS/2002/10/15/";
///
/// 这个内容值包含用于 XML 命名空间的字符串
/// http://msdn.microsoft.com/samples/AYS/2002/10/22/
///
public const string AYS22Oct2002 =
"http://msdn.microsoft.com/samples/AYS/2002/10/22/";
}
最后,第一版的 Web 服务采用了一些其它类别:
· PersonName:这个类别包含三个字符串成员变量分别纪录一个人的名字、中间名和姓氏。
· USAddress:这个类别包含其它成员变量代表街道地址、城市、州和邮政编码。
· Person:这个类别包含两个公用成员,PersonName 和 USAddress。(很惊讶吧!)
所有的类别都使用 System.Xml.Serialization.XmlTypeAttribute 来确定当类别序列化成 XML 时,这些类别是使用相同的 XML 命名空间。在这里以 Person 类别为例子。
【XmlTypeAttribute(Namespace=NamespaceConsts.AYS15Oct2002)】
public class Person {
public PersonName Name;
public USAddress Address;
public override string ToString() {
return string.Format( "{0}\n{1}",
Name.ToString(),
Address.ToString() );
}
}
这里非常详细的说明这个 Web 服务版本 1 的内容。
增加额外的讯息
其中一个更新 Web 服务的方法是增加这个 Web 服务能够接受的额外讯息。该 Web 服务支持数字相加,那么加入数字相减的功能呢?我们要如何增加这个新讯息而不中断现有客户端的联机?首先让我们来看看,如果尝试原有的方法而且只增加一个新的 Web 方法会有什么影响。对于这项试验,我建立一个叫做 Service2a.asmx 的新 Web 服务端点。在这个情况下,版本 2a 的 Web 服务只是被用来显示如何实作这些变更。这些变更也可以应用在版本 1 的 Web 服务。于是我复制 ServiceOne.asmx.cs 的程序代码并且加入这个新的 Web 方法。结果如下。
【WebServiceAttribute(Namespace = NamespaceConsts.AYS15Oct2002)】
public class Version2a : System.Web.Services.WebService {
【WebMethodAttribute】
public string GetDisplayValue( Person person ) {
return person.ToString();
}
【WebMethodAttribute】
public int Add( int a, int b ) {
return a + b;
}
【WebMethodAttribute】
public int Subtract( int a, int b ) {
return a - b;
}
}
如果我接着将客户端指向这个新端点,则 Microsoft® .NET 客户端仍然可以持续地正常执行。这告诉我什么?这告诉我如果增加一个新讯息而不是修改现有的,已经部署的客户端将不需任何修改就可以持续运作。更重要的问题是:「这是正确的作法吗?」从我的观点来看,这个问题的答案是「否定的」。我是从一个已经完全部署 Web 服务的观点严格地说,而不是从您决定如何从 Beta 版操作的观点。
所以,这是正确的作法吗?一般说来,每当你变更这个讯息的定义时,你应该变更这个通讯端口类型 (portType) 的限定名称 (Qualified Name)。「限定名称」是 XML 命名空间加上通讯端口类型名称。
在这个情况下,所有的操作在逻辑上还是相关。把 Add 和 Subtract 作业当作相同 WSDL 通讯端口类型的一部份而将他们系结在一起是十分合理的。Web 服务的使用者会期望有任何 Proxy 产生工具将这些作业保存在一起。在这个例子中,我们想要在当把所有的操作保存在一个系结时,管理由这个输入和输出讯息所使用的 XML 命名空间。我们也想要让现有客户端可以继续存取 Add 和 GetDisplayValue。最后,我们想要指出这个 Web 服务使用一个新的 XML 命名空间。要做到这点,我们需要加入一些属性来设定这个由要求和响应讯息所使用的命名空间。
【WebServiceAttribute(Namespace = NamespaceConsts.AYS22Oct2002)】
public class Version2a : System.Web.Services.WebService {
【WebMethodAttribute】
【SoapDocumentMethodAttribute(
NamespaceConsts.AYS15Oct2002 + "GetDisplayValue",
RequestNamespace=NamespaceConsts.AYS15Oct2002,
ResponseNamespace=NamespaceConsts.AYS15Oct2002 )】
public string GetDisplayValue( Person person ) {
return person.ToString();
}
【WebMethodAttribute】
【SoapDocumentMethodAttribute(
NamespaceConsts.AYS15Oct2002 + "Add",
RequestNamespace=NamespaceConsts.AYS15Oct2002,
ResponseNamespace=NamespaceConsts.AYS15Oct2002 )】
public int Add( int a, int b ) {
return a + b;
}
【WebMethodAttribute】
【SoapDocumentMethodAttribute(
NamespaceConsts.AYS22Oct2002 + "Subtract",
RequestNamespace=NamespaceConsts.AYS22Oct2002,
ResponseNamespace=NamespaceConsts.AYS22Oct2002 )】
public int Subtract( int a, int b ) {
return a - b;
}
}
依预设,Microsoft® ASP.NET Web 服务是根据 SOAPAction 来传送讯息。SOAP 动作是由连结这个 Web 服务的 XML 命名空间和呼叫的作业名称来建立的。所以,如果 Web 服务使用 http://tempuri.org/ 这个 XML 命名空间而且包含一个名为 foo 的作业,预设的 SOAPAction 会是 http://tempuri.org/foo。每个公开的作业会设定 SOAPAction 和命名空间来建立一组更新的作业。这个由 ASP.NET 产生的 WSDL 会将所有的作业放在同一个系结中。作为这个 WSDL 的读取器,我可以看出 Subtract 有时候被加在 Add 和 GetDisplayValue 的后面;作为旧 WSDL 的使用者,原来的客户端会持续运作。任何新的客户端同样能够呼叫 Subtract 并且使用修改过的 XML 命名空间。这个端点不需要中断就能够正确地对旧的和新的客户端来产生响应。然而,这个端点做不到一件重要的事:证明这一个端点支持两个系结。我们要如何做到这点?
因为我们选择建立这个 Web 服务的方式的不同,现有的属性方法,亦即允许开发人员指定特定的作业所属的系结,在这个情况是没有作用的。Add 和 GetDisplayValue 的讯息并不会因为任何方法而改变,意味着我不能只是「正确地」设定属性然后就继续进行。关于这点,您有两个选择:
1. 撰写一些额外的程序代码而且放弃在一个端点上支持两个版本。
2. 将这两个版本的系结信息储存在各自的档案、并为这个端点撰写一个自订的 WSDL 档案,然后关闭这个 Web 应用程序文件。
让我们依序来看这两种选择。
撰写额外的程序代码
这个选择的程序代码存在 Version2b.asmx 中。如果亲手来撰写 WSDL 的主意让你有点却步,你可以另外选择撰写额外的程序代码 (亲手撰写 WSDL 实在不是一件轻松事,而您可能不会想要自寻烦恼)。如果你只是要让 ASP.NET 能够运作,撰写额外程序代码的选择会要求您部署一个新的端点。这个选择并不会维持命名空间和现有客户端的兼容性,意味着这个 Web 应用程序会有两个 .ASMX 端点:一个是版本 1,而另一个是版本 2。
当我开始进行这项工作时,我的第一个直觉是,从 VersionOne 来衍生出版本 2 的 Web 服务类别,以一个新的 XML 命名空间来加入方法,然后将新命名空间中 Add 的要求重新导向到基础类别中的 Add。不管变得更好还是更糟,VersionOne 的方法是可以透过继承而被采用的,而且 Add 和 GetDisplayValue 在这两个命名空间的讯息名称会导致冲突。为什么?因为 ASP.NET 会将相同的讯息名称最后都视为冲突问题。我可以改变这个讯息名称,但是将讯息名称设定为 Addv2 实在很不吸引我。WSDL 档案的 targetNamespace 会显示版本信息,但我不想将这个信息记录在这个作业名称中 -- 这只会让事情变得更混乱。
我的下一个尝试是使用委派。既然功能并没有改变,这个 Web 服务应该可以使用前一版的方法。委派结果可以用。程序代码有三个函式:Add、GetDisplayValue 和新的 Subtract 函式。
【WebServiceAttribute(Namespace = NamespaceConsts.AYS22Oct2002)】
public class Version2b : WebService {
【WebMethodAttribute】
public string GetDisplayValue( Person person ) {
// 呼叫原始函式
VersionOne v1 = new VersionOne();
return v1.GetDisplayValue( person );
}
【WebMethodAttribute】
public int Add( int a, int b ) {
// 呼叫原始函式
VersionOne v1 = new VersionOne();
return v1.Add( a, b );
}
【WebMethodAttribute】
public int Subtract( int a, int b ) {
return a - b;
}
}
如您所见,对 Add 和 GetDisplayValue 的呼叫会委派原来版本的要求。
自订的 WSDL
让我们来看一看,可以怎么样证明 Service2a.asmx 已经实作版本 1 和 版本 2 的系结。一个 WSDL 档案以 /documentation/@targetNamespace 属性来表示它的版本,在版本 1 中 targetNamespace 属性的内容值是 http://msdn.microsoft.com/samples/AYS/2002/10/15/,在版本 2 则是使用 http://msdn.microsoft.com/samples/AYS/2002/10/22/。
目前我能确知的是,不可能找到方法让 ASP.NET 自动产生正确的 WSDL。这里是我们接下来的步骤:
1. 储存 VersionOne.asmx Web 服务的 WSDL。
2. 储存 Version2a.asmx Web 服务的 WSDL。
3. 从 WSDL 文件中移除服务元素。
4. 将版本 1 命名空间的结构描述 (Schema) 放进各自的 XSD 档案。
5. 撰写并储存 Version2a.asmx Web 服务的 WSDL 档案。
6. 将文件关闭。
步骤 1 到 3 可以由联机到 .ASMX 网页、检视 WSDL 然后储存所呈现的 WSDL 到磁盘来完成。然后开启储存的档案而且移除服务组件。在步骤 4,我建立一个新档案 MessageTypes.xsd,并将 VersionOne.wsdl 中型别区段的内容存到这个档案,然后加入这个 XSD XML 命名空间和版本 1 XML 命名空间的命名空间宣告。最后,VersionOne.wsdl 的型别区段被简化如下:
namespace="http://msdn.microsoft.com/samples/AYS/2002/10/15/"
location="http://localhost/AYS15Oct2002/MessageTypes.xsd" />
这个 Web 服务的新 WSDL 是:
xmlns:s1="http://msdn.microsoft.com/samples/AYS/2002/10/22/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:s0="http://msdn.microsoft.com/samples/AYS/2002/10/15/"
targetNamespace="http://msdn.microsoft.com/samples/AYS/2002/10/Impl"
xmlns="http://schemas.xmlsoap.org/wsdl/">
location="http://localhost/AYS15Oct2002/VersionOne.WSDL" />
location="http://localhost/AYS15Oct2002/Version2a.WSDL" />
location="http://localhost/AYS15Oct2002/Version2a.asmx" />
location="http://localhost/AYS15Oct2002/Version2a.asmx" />
注意到这两个连接端口的位置是相同的。最后关闭这个文件。要做到这点,必须在 web.config 中的 /configuration/system.web/webServices/protocols 区段加入下面几行:
那么这个选择完成了什么事情?我们修改了 WSDL,让它准确反映两个 Web 服务版本都接受的讯息。建立的新 WSDL 显示这个端点了解两份文件中所定义的讯息。这个基础 Web 服务因其编码方式而能够接受两种版本的讯息。
增加新方法不是开发 Web 服务的唯一方式。接下来,让我们来看假使您想变更方法签名码时,要如何处理。
变更方法签名码
在这个例子中,我假设您想要让现有版本仍然能够运作,而且您想要改变特定讯息的内容。让我们来看看改变 Add 讯息以接受一个整数数组,然后传回这些整数的总和。这个新的 Add 讯息和旧的是不兼容的。
我可以赋予这两个讯息不同的名称使它们都在同一个端点操作,但这不是我想要做的。这个 WSDL 需要反映出这个方法是较新且较佳的 Add 的形式。为了让这项分别更清楚,并与其它函式一起运作,我建立一个新的 Web 服务叫做版本 2c。我也决定将 GetDisplayValue 移到新的 XML 命名空间。这个函式还是呼叫内部的 GetDisplayValue 方法来利用现有的程序代码和未来的错误修正。
【WebServiceAttribute(Namespace=NamespaceConsts.AYS22Oct2002)】
【WebServiceBinding("Version2", NamespaceConsts.AYS22Oct2002 )】
public class Version2c : System.Web.Services.WebService {
【WebMethodAttribute】
【SoapDocumentMethodAttribute(Binding="Version2")】
public string GetDisplayValue( Person person ) {
VersionOne v1 = new VersionOne();
return v1.GetDisplayValue( person );
}