版本控制(Revision control)是一種軟件工程技巧,籍以在開發的過程中,確保由不同人所編輯的同一檔案都得到更新。
名詞定義
版本控制是指通過文檔控制(documentation control)記錄程序各個模組的改動,并為每次改動編上序號。這種方法是工程圖(engineering drawings)維護(maintenance)的標準做法,它伴隨著工程圖從圖的誕生一直到圖的定型。一種簡單的版本控制形式,例如,賦給圖的初版一個版本等級“A”,當做了第一次改變后,版本等級改為“B”,以此類推等等。
相關系統
1.軟件系統的版本控制是指可以自行運行的各子系統的版本控制。
2.軟件系統的版本號由評測小組的人員確定,由評測小組進行版本控制工作。
3.軟件系統的版本號由3部分構成,即主版本號+次版本號+修改號。主版本號1位,只有當系統在結構和功能上有重大突破改進后才發生變化;次版本號有2位;修改號8位,采用提交時的日期,當系統進行任何修改后,包括數據庫結構發生變化,修改號都要隨之改變。例如:Ver3.31.19990317。
4.各子系統的版本號獨立。
5.各軟件系統應該有顯示詳細版本號的功能。例如help菜單下的about功能。系統提交存檔時,評測服務部要進行版本號檢查。
6.新系統開發完成、或已存檔的系統進行修改,修改完成后,進行提交存檔時,由評測評測小組系統分析工程師確定新版本號、或更改版本號。
7.軟件系統,產生新的版本后,老版本的軟件系統是否繼續保存,取決于以下條件:
a.老版本的系統如果有客戶還在使用,在客戶升級以前,必須繼續保存。
b.老版本的系統已經沒有客戶使用了,并且新版本的系統已經把老系統的文檔完整地升級過來,這樣可以刪除或復蓋老版本的系統資源。
c.對于要刪除或復蓋的老版本系統,可以統一備份起來。
控制系統分類
本地版本
許多人習慣用復制整個項目目錄的方式來保存不同的版本,或許還會改名加上備份時間以示區別。這么做唯一的好處就是簡單,不過壞處卻不少:有時候會混淆所在的工作目錄,弄錯了文件丟了數據就沒了退路。
為了解決這個問題,人們很久以前就開發了許多種本地版本控制系統,大多都是采用某種簡單的數據庫來記錄文件的歷次更新差異。
其中最流行的一種叫做美國廣播資訊化服務公司,現今許多計算機系統上都還看得到它的蹤影。甚至在流行的MacOSX系統上安裝了開發者工具包之后,也可以使用rcs命令。它的工作原理基本上就是保存并管理文件補丁(Patch)。文件補丁是一種特定格式的文本文件,記錄著對應文件修訂前后的內容變化。所以,根據每次修訂后的補丁,rcs可以通過不斷打補丁,計算出各個版本的文件內容。
集中化版本
接下來人們又遇到一個問題,如何讓在不同系統上的開發者協同工作?于是,集中化的版本控制系統(Centralized Version ControlSystems,簡稱CVCS)應運而生。這類系統,諸如CVS,Subversion以及perforce等,都有一個單一的集中管理的服務器,保存所有文件的修訂版本,而協同工作的人們都通過客戶端連到這臺服務器,取出最新的文件或者提交更新。多年以來,這已成為版本控制系統的標準做法。
這種做法帶來了許多好處,特別是相較于老式的本地VCS來說。現在,每個人都可以一定程度上看到項目中的其他人正在做些什么。而管理員也可以輕松掌控每個開發者的權限,并且管理一個CVCS要遠比在各個客戶端上維護本地數據庫輕松容易得多。
事分兩面,有好有壞。這么做最顯而易見的缺點是中央服務器的單點故障。若是服務器宕機一小時,那么在這一小時內,誰都無法提交更新,也就無法協同工作。如果中央服務器的磁盤發生故障,并且沒做過備份或者備份得不夠及時的話,還會有丟失數據的風險。最壞的情況是徹底丟失整個項目的所有歷史更改記錄,被客戶端提取出來的某些快照數據除外,但這樣的話依然是個問題,你不能保證所有的數據都已經有人提取出來。本地版本控制系統也存在類似問題,只要整個項目的歷史記錄被保存在單一位置,就有丟失所有歷史更新信息的風險。
分布式版本
于是分布式版本控制系統(Distributed Version Control System,簡稱DVCS)面世了。在這類系統中,諸如Git,Mercurial,BAZAAR還有Darcs等,客戶端并不只提取最新版本的文件快照,而是把原始的代碼倉庫完整地鏡像下來。這么一來,任何一處協同工作用的服務器發生故障,事后都可以用任何一個鏡像出來的本地倉庫恢復。因為每一次的提取操作,實際上都是一次對代碼倉庫的完整備份。
更進一步,許多這類系統都可以指定和若干不同的遠端代碼倉庫進行交互。籍此,你就可以在同一個項目中,分別和不同工作小組的人相互協作。你可以根據需要設定不同的協作流程,比方說層次模型式的工作流,這在以前的集中式系統中是無法實現的。
詳細內容
版本控制包括兩個方面:保正人人得到的是最新的版本,記錄需求的歷史版本。
如果有專門的需求管理商業工具可以助您一臂之力,由于我并沒有條件試用所有的需求管理工具,能夠向大家推薦的只有瑞理公司的RequisitePro,推薦的一個重要原因是它能夠把需求和瑞理的其他工具如Rose、TeamTest等聯系起來,從而實現需求鏈。
能夠借助工具將需求自動化固然很好,不過,工具使用不當也不會提高生產效率。需求管理的工具其實用簡單的Microsoft Office和任一個關系型數據庫就可以解決,而且根據企業自身的特點,摸索出最適合企業用的工具。
版本控制的最簡單方法是在每一個公布的需求文檔的版本應該包括一個修正版本的歷史情況,即已做變更的內容、變更日期、變更人的姓名以及變更的原因并根據標準約定手工標記軟件需求規格說明的每一次修改。
業務流程
利用WebLogic Workshop的版本控制功能,能夠在不中斷當前正在運行的任何流程實例的情況下對業務流程進行更改。對業務流程進行版本控制時,便是創建了業務流程的子版本,該版本與其父版本共享同一公共URI(接口)。運行時,標記為有效的流程版本便是將由外部客戶端通過公共URI來訪問的流程。
注意:可以對業務流程進行版本控制,但無法對與該流程關聯的單個控件或其他與業務流程有關的組件(如 schema和轉換)進行版本控制。對業務流程進行版本控制時,還必須對該流程的子流程進行版本控制,因為對父流程進行版本控制時,該控制對其子流程無效。
關鍵術語
簽入文件或目錄:此操作將工作目錄作為新版本復制回存儲庫。
簽出文件或目錄:此操作從存儲庫中將文件的最新修訂版本復制到工作空間。簽出目錄時,將簽出該目錄下的所有文件和子目錄。
提交文件或目錄:此操作與簽入文件或目錄相同。版本控制用戶會經常說他們“已提交更改”;這表示他們對各自文件的工作副本做了更改,并將這些更改提交到存儲庫。
沖突:當兩名開發人員對同一文件的工作副本進行更改,并將這些更改提交到存儲庫時,他們的工作可能會發生沖突。在這種情況下,CVS或Subversion將檢測沖突,并要求某個人先解決該沖突,然后再提交他們的更改。
合并:將對相同文件的不同工作副本進行的多個更改合并到源存儲庫中。合并是一種管理沖突的策略,它允許多名開發人員同時工作(不必對文件進行鎖定),然后將他們的工作并入一個組合版本中。當對同一文件的不同行進行兩組更改時,合并這兩組更改很容易,而合并操作也可正常進行。但對文件的同一行或幾行進行更改時,將發生沖突,這就要求有人手動編輯該文件,然后才能將這些更改成功提交到源存儲庫。
存儲庫:具有受版本控制的所有文件的完整修訂歷史的共享數據庫。
解決:當兩名開發人員試圖提交發生沖突的更改,而造成文件內的沖突時,必須通過手動編輯該文件進行處理。必須有人逐行檢查該文件,以接受一組更改并刪除另一組更改。除非沖突解決,否則存在沖突的文件無法成功提交到源存儲庫中。
修訂版本:對各個文件進行具體更新的編號草案。每次編輯文件并將它提交回存儲庫時,該文件的修訂版本號將會增加。
版本:用于標識文件集的編號方案,可在某個時間點標記并命名這些文件集。
工作空間:要在本地硬盤或 Unix 用戶帳戶上編輯的文件副本。在工作空間中編輯文件時,這些文件將不再與存儲庫同步。這就是進度!然后您需要將更改返回存儲庫,以便其他人可以看到這些更改。
Subversion:詞匯表
APR:Subversion置于稱為APR(apache可移植運行庫)的可移植層上。這意味著Subversion應該在任何運行Apache httpd的操作系統上工作:Windows、Linux、BSD 的所有 flavors、Mac OS、NetWare以及其它操作系統。
分支:分支是指目錄和文件的現有原始樹的副本。分支的生命周期是從某事物的副本開始的,并從此副本處移動,生成自己的歷史。通常創建分支以嘗試新功能,同時不影響具有編譯器錯誤和小問題的開發的主分支。
檢出:檢查存儲庫,會在本地計算機上創建所需分支的副本。此副本包含了您指定的存儲庫的最新版本。
提交:文件的提交意味著已將對本地副本所做的更改更新到存儲庫中。提交文件后,用戶可以查看對特定文件執行“更新”后的最新版本。
Hook:是被存儲庫事件觸發的程序,例如創建新版本或修改無版本屬性。 Hook 中保留了足夠的信息,可以告知該事件是什么、正被操作的目標是什么,以及觸發此事件的人員的用戶名是什么。
鎖定:是指一種機制,在此機制下,用戶請求獲得修改工作副本文件的更改的專有權限。
合并:是指將某分支上的更改聯接到此主干或同為主干的另一個分支。
存儲庫:Subversion的核心為存儲庫。它是一個存儲和共享數據的集中式系統。存儲庫以一組樹和分支的形式(即目錄和文件的層次結構)存儲信息。任何數量的客戶端都可以連接到存儲庫中,并對這些文件進行讀取和寫入。
存儲庫瀏覽器:在某些情況下中,可能需要直接在存儲庫中工作而不使用工作副本。這便是存儲庫瀏覽器的由來。它與File Explorer窗口具有同樣的圖標以及用于鍵入將顯示的存儲庫 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.微軟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.微軟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;
}
}
如果我接著將客戶端指向這個新端點,則 微軟? .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;
}
}
依預設,微軟? 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.微軟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.微軟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.微軟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 );
}
參考資料 >