词条 | Revision control |
释义 | 中科永联高级技术培训中心(www.itisedu.com) 版本控制(Revision control)是一种软体工程技巧,籍以在开发的过程中,确保由不同人所编辑的同一档案都得到更新。 版本控制透过文档控制(documentation control)记录程序各个模组的改动,并为每次改动编上序号。这种方法是维护(maintenance) of 工程图(engineering drawings)的标准做法, 它伴随着工程图从图的诞生一直到图的定型。 一种简单的版本控制形式,例如,赋给图的初版一个版本等级“A”。当做了第一次改变后,版本等级改为“B”,以此类推等等。. 版本控制系统用于维护文件的所有版本,随着时间的推移,文件逐渐产生这些版本。使用版本控制系统,人们可以返回到各个文件以前的修订版本,还可以比较任意两个版本以查看它们之间的变化。通过这种方式,版本控制可以保留一个文件修订的可检索的准确历史日志。更重要的是,版本控制系统有助于多个人(甚至位于完全不同的地理位置)通过 Internet 或 专用网将各自的更改合并到同一个源存储库,从而协同开发项目。 版本控制包括两个方面:保正人人得到的是最新的版本,记录需求的历史版本。 如果有专门的需求管理商业工具可以助您一臂之力,由于我并没有条件试用所有的需求管理工具,能够向大家推荐的只有瑞理公司的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}\{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 区段加入下面几行:sp; 那么这个选择完成了什么事情?我们修改了 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 ); } 【WebMethodAttribute】 【SoapDocumentMethodAttribute(Binding="Version2")】 public int Add( int【】 values ) { int retval = 0; if ( ( values == null ) || ( values.Length < 1 ) ) { // No values passed in, return 0. return retval; } foreach( int val in values ) { retval += val; } return retval; } } 在这个情况下,版本 1 和版本 2 的客户端在不同的 URL 并排执行。Add 方法却不能混淆两个版本,因为一个版本接受一个整数数组,而旧的版本接受两个整数。 最后让我们看看当您增加新的衍生型别而且想要这个 Web 方法来处理新资料时,要做什么事来作一个总结。 更新数据模型 假使您对为什么 GetDisplayValue 是在这个 Web 服务感到困惑,您差不多要揭晓了。在我们的实例中,我们决定增加一个新的 Person 型别叫做 Penpal。Penpal 透过增加电子邮件地址来扩充 Person 。Penpal 的定义是: 【XmlType(Namespace=NamespaceConsts.AYS22Oct2002)】 public class Penpal : Person { public string Email; public override string ToString() { return string.Format( "{0}\{1}", base.ToString(), Email ); } } 我希望这个 Web 服务能接受 Person 型别或者是 GetDisplayValue 的 Penpal 型别。我对这个 Web 服务做了下列事情: · 增加一个新型别。 · 改变成 GetDisplayValue 能接受的型别。 考量我的需求,我似乎身处增加一个新方法的时候和我改变这个签名码的地方之间。然而,我的确改变了 GetDisplayValue 方法能接受的数据类型集合。如果版本控制是非常重要 (事实上它是),这个改变需要 GetDisplayValue 在一个新的 XML 命名空间。所以再一次,这个新版本应该是一个独立的 URL 和类别。 【WebServiceAttribute(Namespace=NamespaceConsts.AYS22Oct2002)】 【WebServiceBindingAttribute("Version2", NamespaceConsts.AYS22Oct2002)】 public class Version2d : System.Web.Services.WebService { 【WebMethodAttribute】 【SoapDocumentMethodAttribute(Binding="Version2")】 public string GetDisplayValue( 【XmlElement("Person", typeof(Person))】 【XmlElement("Penpal", typeof(Penpal))】 Person person ) { VersionOne v1 = new VersionOne(); return v1.GetDisplayValue( person ); } 【WebMethodAttribute】 【SoapDocumentMethodAttribute(Binding="Version2")】 public int Add( int a, int b ) { VersionOne v1 = new VersionOne(); return v1.Add( a, b ); } } 本质上,这个修正版本会对内呼叫旧的 Web 服务,因为功能没有改变 -- 只有 WSDL。 结论 当建立一个新版 Web 服务时,你将需要进行一处或多处修改: · 增加一个新方法。 · 变更方法签名码。 · 更新数据模型 在每一个案例中,要建立一个新的 Web 服务端点,最简单的方法是尽可能委派之前的版本,只实作新的功能。这不是我们第一次讨论版本控制,但这确实是我第一次展示如何用程序代码来完成这些工作。一些读者张贴了一些相当好的问题,促成这篇文章的大部分内容。感谢这些很棒的回馈。 要注意的另一点是,这是我在〈At Your Service〉系列专栏中所写的最后一篇文章。我的工作职责已有所改变,而且我不再有什么时间可以贡献给这个专栏。我真的很高兴有机会在这里出现,并向我的读者展示有关我学习 ASMX 和 SOAP 交互操作性的经验。 四、版本控制工具SVN和CVS CVS相信大家都听说过,不过这个广为使用的版本控制工具还有不少问题,包括中文支持和二进制文件的处理都有或多或少的问题。 现在好了,CVS的作者又为我们开发了SVN。Gnome和KDE的开发团队都已经换用SVN了,您为什么不试一下呢? § 最基本的用法 建立代码库 svnadmin create /path/to/repos 导入数据 svn import /path/to/project file::///path/to/repos -m "initial import" 导出数据 svn checkout file::///path/to/repos 提交更新 svn commit filename 添加文件 svn add 删除文件 svn delele 复制文件 svn copy 移动文件 svn move 查询状态 svn status 检查不同 svn diff 同步工作目录 svn update 合并代码 svn merge;svn resolve § SVN的相关资源 这里是SVN的项目网站。 <>的电子书。 这里是繁体中文的SVN文档(只有部分被翻译过来了)。 § 不能不提的CVS 虽然我已经转入了SVN的阵营,但是CVS仍然是应用最为广泛的版本控制软件之一。这里收集了一些关于CVS的资源。 CVSD的安装与配置 CVS使用简介 (注:这是oscargreat整理的资料,我拿来用相信他不会介意:) 什么是CVS CVS(Cocurrent Version Systems,并发版本系统)是一个C/S模式的版本控制系统,用于在软件开发过程中记录文件版本,协调开发人员保证文件同步,从而保证项目正确的进行并行开发,并支持版本回滚、bug 跟踪和补丁生成。使用CVS可以有效地对软件开发的源代码和开发文档进行统一的管理和组织。 CVS的工作模式: CVS的基本工作模式如下: CVS服务器(代码文档库) / | (版 本 同 步) / | 开发者1 开发者2 开发者3 CVS在服务器端维护代码文档库,不同的开发者在本地机器上建立对应代码树,并利用CVS保持本地代码文档同代码文档库的一致。当由于多个开发者对文件的同时修改造成本地与库中的代码文件冲突时,CVS报告并协助解决冲突代码的合并问题。普通开发者(非管理员)对CVS的使用流程如下所示: Check out(获取) -------------------- Merge(合并) | | ^ v v Conflict(冲突) | Modify(修改)-> Update(更新) ---------------- ^ | | | No Conflict(无冲突) | v Update(更新) <- Commit(提交) | v Export(导出) check out命令只需在开始建立本地代码树时使用一次,其后更新本地代码则使用update命令。update命令比较服务器和本地代码库的区别,并把本地代码树中过时的文件自动更新。当完成对代码的修改之后,在提交代码之前同样需要使用update命令,以获取他人并行修改的的代码。如果出现冲突(即对同一文件同时进行了修改),CVS将在本地代码中把两者都保留并标记出来,要求开发者处理冲突。在冲突不存在或已解决的情况下,使用commit命令将服务器代码更新为本地代码。CVS要求为更改提供注释,并自动为更新的文件处理版本编号。当软件需要正式发布时,使用export命令导出不包含CVS设置信息的源代码树。 CVS的管理员还使用包括init, import, admin等命令对服务器和代码库进行?户端的使用 Linux下的多种IDE/Editor,如Emacs,Eclipse等都对CVS提供了支持,但基于命令行的cvs操作是最为基本和灵活的。以下介绍CVS命令行的使用。 环境变量 CVSROOT 指定代码库的位置 如果CVS代码库在本地机器上,可直接指定代码库的路径,如: export CVSROOT=/path/to/cvsroot 如果CVS代码库在服务器上,则还需指定服务器位置,通信方式 及用户等信息,格式为: CVSROOT=:method:username@cvs.server.address#port:/path/to/cvsroot 例如: export CVSROOT=:ext:horn@166.111.55.119:/cvs/horn 其中ext指定使用SSH协议,horn是有权访问服务器相应目录的 用户。 CVSROOT的值可以在命令行上用-d选项重新指定,如: cvs -d /cvs/horn update CVS_RSH 指定客户端访问服务器的协议 使用SSH协议时,可如下设置: export CVS_RSH=ssh 基本命令 cvs的命令行格式为: cvs 【options】 command 【options】 filename 具体参数可参考info cvs cvs的命令如果不带参数,则总是以当前所在目录作为操作对象。 以下介绍基本命令: init CVS代码库的初始化,管理员使用。 cvs -d /cvs/horn init 将/cvs/horn初始化为一个代码库 import 导入一个项目/模块,管理员使用。 cvs import -m "comments" project_name vendor_tag release_tag 执行后,会将当前目录下所有文件及目录导入到 /path/to/cvsroot/project_name 目录下。 vender_tag: 开发商标记 release_tag: 初始版本标记 -m 参数如果不加,则cvs会自动启动vi,要求输入注释。 如: cd /home/horn/blob-2.05/ cvs import blob Hornworks InitVersion checkout/co 从服务器获取代码,在本地建立代码树 cvs checkout project_name update/up 将本地文件同步到最新的版本 cvs update filename 不指定文件名,cvs将当前目录下所有子目录下的文件。如前 所述,在每天工作前和工作之后commit之前都应当update,以 保证本地代码总是最新的,且和服务器的代码无冲突。 commit/ci 将修改同步到CVS库里 cvs commit -m "write some comments here" file_name CVS的很多动作都是通过cvs commit进行最后确认并修改的。 在确认的前,还需要用户填写修改注释,以帮助其他开发人员 了解修改的原因。 add 向项目中添加文件/目录 cvs add new_file 添加文件之前应当首先创建文件,之后使用cvs add添加。添 加文件的操作只有经过cvs commit之后才真正被添加到代码库 中。对于图片,Word文档等非纯文本的项目,需要使用 cvs add -kb 选项按二进制文件方式导入(k表示扩展选项,b表示 binary),否则有可能出现文件被破坏的情况。 remove/rm 从项目中删除文件 cvs remove file_name 删除时,应当先将某个源文件物理删除后,再使用remove命令。 比如: rm file_name cvs remove file_name 然后commit确认删除。 也可以加上-f参数将两步合一: cvs remmove -f file_name cvs commit -m "why delete file" cvs不允许删除目录,空目录在update时会依选项自动忽略。 log/history 查看修改历史 cvs log file_name diff 查看文件不同版本的区别 cvs diff -r1.3 -r1.5 file_name 查看1.3版本何1.5版本的区别 cvs diff file_name 查看本地和库中文件的区别 tag 标记版本号 cvs tag release_version CVS自动维护每个文件的版本号,文件每修改一次,则其版本 号自动增加。此版本号不能用作阶段性发布使用。tag命令为 当前目录下所有文件标记一个统一的发行版本号。 如: cd blob/ cvs tag 2.1.0-Hornworks tag命令应当由项目负责人统一指定和使用。 export 项目发布, 导出不带CVS目录的源文件 本地代码树的每个目录下,CVS都创建了一个CVS/目录用于记 录当前目录和CVS库之间的对应信息。export可以导出不包含 CVS目录的代码树。 cvs export -r release project_name 导出版本号标记为 release的代码 cvs export -D 20021023 project_name 导出截至2002.10.23时最新的文件 |
随便看 |
百科全书收录594082条中文百科知识,基本涵盖了大多数领域的百科知识,是一部内容开放、自由的电子版百科全书。