使用 .NET Framework 簡化部署和解決 DLL 地獄
Steven Pratschner
Microsoft Corporation
更新日期:2001 年 11 月
總結:本文介紹元件的概念,並說明.NET Framework如何使用元件來解決版本設定和部署問題。 (16 個列印的頁面)
目錄
簡介
問題陳述
解決方案的特性
元件:建置組塊
版本設定和共用
版本原則
部署
總結
簡介
Microsoft® .NET Framework引進了數項新功能,旨在簡化應用程式部署和解決 DLL Hell。 使用者和開發人員都熟悉現今元件型系統可能發生的版本設定和部署問題。 例如,幾乎每位終端使用者在其電腦上安裝了新的應用程式,只發現現有的應用程式會停止運作。 大部分開發人員也花在 Regedit 上花費時間,嘗試讓所有必要的登錄專案保持一致,以啟用 COM 類別。
.NET Framework中用來解決 DLL Hell 的設計指導方針和實作技術,是以 Microsoft Windows® 2000 中完成的工作為基礎,如 Rick Anderson 在DLL 的結尾,以及 David D'Souza、BJ Overviewn 和 Peter Wilson 在應用程式中實作並存元件共用 (展開) 中所述。 .NET Framework提供功能,包括應用程式隔離和 .NET 平臺上使用 Managed 程式碼所建置的應用程式並存元件,藉此擴充此工作。 此外,請注意,Windows XP 為 Unmanaged 程式碼提供相同的隔離和版本控制功能,包括 COM 類別和 Win32 DLL (請參閱 如何建置和服務隔離的應用程式,以及 Windows XP 的並存元件 ,以取得詳細資料) 。
本文介紹 元件 的概念,並說明 .NET 如何使用元件來解決版本設定和部署問題。 特別是,我們將討論群組件的結構、其命名方式,以及編譯器和 Common Language Runtime (CLR) 如何使用元件來記錄及強制執行應用程式片段之間的版本相依性。 我們也會討論應用程式和系統管理員如何透過我們呼叫 版本原則來自訂版本控制行為。
在導入並描述元件之後,將會呈現數個部署案例,並提供.NET Framework中可用的各種封裝和散發選項取樣。
問題陳述
版本控制
從客戶的觀點來看,最常見的版本控制問題就是我們稱為 DLL Hell。 簡單來說,DLL Hell 是指當多個應用程式嘗試共用一般元件,例如動態連結程式庫 (DLL) 或元件物件模型 (COM) 類別時所造成的問題集。 在最常見的案例中,一個應用程式會安裝與電腦上版本不相容的新版本共用元件。 雖然剛安裝的應用程式可以正常運作,但相依于舊版共用元件的現有應用程式可能無法再運作。 在某些情況下,問題的原因會更細微。 例如,請考慮使用者下載 Microsoft ActiveX® 控制項作為造訪某些網站的副作用的案例。 下載控制項時,它會取代電腦上存在的任何現有控制項版本。 如果電腦上已安裝的應用程式發生使用此控制項,可能也會停止運作。
在許多情況下,使用者發現應用程式已停止運作之前,會有顯著的延遲。 因此,對可能會影響應用程式的機器進行變更時,通常很難記住。 使用者可能記得在一周前安裝某個專案,但該安裝與他們現在看到的行為之間沒有明顯的相互關聯。 為了更糟,目前有幾個診斷工具可協助使用者 (或協助其支援人員) 判斷錯誤。
這些問題的原因是系統不會記錄或強制執行應用程式不同元件的版本資訊。 此外,代表一個應用程式對系統所做的變更通常會影響機器上的所有應用程式—現今建置完全與變更隔離的應用程式並不簡單。
建置隔離應用程式的原因之一是目前的執行時間環境通常只允許安裝單一版本的元件或應用程式。 這項限制表示元件作者必須以保持回溯相容的方式撰寫其程式碼,否則它們會在安裝新元件時,可能會中斷現有的應用程式。 在實務上,如果不可能,撰寫永遠與回溯相容的程式碼非常困難。 在 .NET 中,並 存 的概念是版本設定本文的核心。 並存是同時在機器上安裝和執行相同元件的多個版本的能力。 透過支援並存的元件,作者不一定會系結至維持嚴格的回溯相容性,因為不同的應用程式可以自由使用不同版本的共用元件。
部署和安裝
現今安裝應用程式是一個多步驟程式。 一般而言,安裝應用程式牽涉到將許多軟體元件複製到磁片,以及建立一系列登錄專案,以描述這些元件至系統。
登錄中的專案與磁片上的檔案之間的區隔,使得複寫應用程式並將它們卸載非常困難。 此外,在登錄中完整描述 COM 類別所需的各種專案之間的關聯性非常鬆散。 這些專案通常包含 coclasses、interface、typelibs 和 DCOM 應用程式識別碼的專案,而不要提及註冊檔延伸模組或元件類別的任何專案。 您最終會手動保持這些同步。
最後,需要此登錄使用量才能啟用任何 COM 類別。 這大幅使部署分散式應用程式的程式變得複雜,因為必須觸控每個用戶端電腦,才能進行適當的登錄專案。
這些問題主要是因為元件的描述與元件本身分開所造成。 換句話說,應用程式不是自我描述或獨立式。
解決方案的特性
.NET Framework必須提供下列基本功能,才能解決剛才描述的問題:
- 應用程式必須自我描述。 自我描述的應用程式會移除登錄上的相依性,啟用零影響安裝並簡化卸載和複寫。
- 版本資訊必須記錄並強制執行。 版本控制支援必須內建于平臺,以確保在執行時間載入適當的相依性版本。
- 必須記住「最後一個已知良好」。 當應用程式成功執行時,平臺必須記住一組元件,包括其版本一起運作。 此外,必須提供工具,讓系統管理員能夠輕鬆地將應用程式還原為這個「最後已知良好」狀態。
- 支援並存元件。 允許在機器上同時安裝及執行多個元件的版本,可讓呼叫端指定想要載入的版本,而不是不小心「強制」版本。 .NET Framework讓多個版本的架構本身共存于單一電腦上,以進一步並存。 這可大幅簡化升級案例,因為如有需要,系統管理員可以選擇在不同的版本的.NET Framework上執行不同的應用程式。
- 應用程式隔離。 .NET Framework必須讓應用程式更容易且事實上是預設值,才能撰寫代表其他應用程式對電腦所做的變更所影響的應用程式。
元件:建置組塊
元件是.NET Framework用來解決剛才描述的版本設定和部署問題的建置組塊。 元件是類型和資源的部署單位。 元件在許多方面等同于現今世界中的 DLL;基本上,元件是「邏輯 DLL」。
元件是透過稱為資訊清單的中繼資料進行自我描述。 就像 .NET 使用中繼資料來描述類型一樣,它也會使用中繼資料來描述包含類型的元件。
元件與部署有關。 例如,.NET 中的版本控制是在元件層級完成,而沒有較小的版本,例如模組或類型,會設定版本。 此外,元件可用來在應用程式之間共用程式碼。 類型所包含的元件是型別身分識別的一部分。
程式碼存取安全性系統會在其許可權模型的核心使用元件。 資訊清單中元件記錄的作者是執行程式碼所需的許可權集,而系統管理員會根據包含程式碼的元件,將許可權授與程式碼。
最後,元件也是類型系統和執行時間系統的核心,因為它們會建立類型的可見度界限,並做為解析型別參考的運行時間範圍。
元件資訊清單
具體而言,資訊清單包含關於元件的下列資料:
- 身分識別。 元件的身分識別包含四個部分:簡單文字名稱、版本號碼、選擇性文化特性,以及為共用建置的選擇性公開金鑰, (請參閱下方的共用元件一節) 。
- 檔案清單。 資訊清單包含組成元件的所有檔案清單。 針對每個檔案,資訊清單會在建置資訊清單時記錄其內容的名稱和密碼編譯雜湊。 此雜湊會在執行時間進行驗證,以確保部署單位一致。
- 參考的元件。 元件之間的相依性會儲存在呼叫元件的資訊清單中。 相依性資訊包含版本號碼,其會在執行時間使用,以確保載入正確的相依性版本。
- 匯出的類型和資源。 類型和資源可用的可見度選項包括「只在我的元件內可見」和「對元件外部呼叫者可見」。
- 許可權要求。 元件的許可權要求會分成三組:1 個) 元件執行所需的許可權要求、2 個) 元件仍會有某些功能,即使未授與元件,3 個) 作者從未想要授與元件的元件。
IL 反組譯程式 (Ildasm) SDK 工具適用于查看元件中的程式碼和中繼資料。 圖 1 是 Ildasm 所顯示的範例資訊清單。 .assembly指示詞會識別元件和.assembly extern指示詞,其中包含此元件相依之其他元件的相關資訊。
圖 1. IL 反組譯程式所顯示的範例資訊清單
元件結構
到目前為止,元件主要描述為邏輯概念。 本節藉由描述元件實際呈現的方式,協助讓元件更具體。
一般而言,元件包含四個元素:元件中繼資料 (資訊清單) 、描述類型的中繼資料、中繼語言 (IL) 實作型別的程式碼,以及一組資源。 並非所有這些都存在於每個元件中。 只有資訊清單是嚴格必要,但需要類型或資源,才能提供元件任何有意義的功能。
有數個選項可用來「封裝」這四個元素。例如,圖 2 顯示包含整個元件的單一 DLL:資訊清單、類型中繼資料、IL 程式碼和資源。
圖 2. 包含所有元件專案的 DLL
或者,元件的內容可能會分散到多個檔案中。 在圖 3 中,作者已選擇將某些公用程式程式碼分成不同的 DLL,並保留大型資源檔 (在此情況下,JPEG) 在其原始檔案中。 其中一個原因可能是優化程式碼下載。 .NET Framework只會在參考檔案時下載檔案,因此,如果元件包含不常存取的程式碼或資源,將它們分成個別檔案將會提高下載效率。 另一個使用多個檔案的常見案例是建置包含多個語言之程式碼的元件。 在此情況下,您會個別建置每個檔案 (模組) ,然後使用 .NET Framework SDK (al.exe) 中提供的元件連結器工具將它們分組成元件。
圖 3. 跨多個檔案散佈的元件元素
版本設定和共用
DLL Hell 的主要原因之一是目前在元件型系統中使用的共用模型。 根據預設,個別軟體元件是由電腦上的多個應用程式共用。 例如,每當安裝程式將 DLL 複製到系統目錄或註冊 COM 登錄中的類別時,該程式碼可能會對電腦上執行的其他應用程式造成影響。 特別是,如果現有的應用程式使用了舊版的共用元件,該應用程式將會自動開始使用新版本。 如果共用元件嚴格回溯相容,可能沒問題,但在許多情況下,如果無法維持回溯相容性,則很困難。 如果未維護回溯相容性或無法維護,這通常會導致應用程式因安裝其他應用程式的副作用而中斷。
.NET 中的原則設計指導方針是隔離元件 (或元件) 。 隔離元件表示元件只能由一個應用程式存取,它不會由電腦上的多個應用程式共用,而且無法受到其他應用程式對系統所做的變更所影響。 隔離可讓開發人員絕對控制其應用程式所使用的程式碼。 隔離或應用程式私用元件是 .NET 應用程式中的預設值。 從 Microsoft Windows 2000 開始,引進 .local 檔案的隔離元件趨勢。 此檔案用來在嘗試找出要求的元件時,先在應用程式目錄中查看 OS Loader 和 COM。 (請參閱 MSDN 程式庫中的相關文章: 在 Applications 中實作並存元件共用。)
不過,在某些情況下,必須在應用程式之間共用元件。 對於每個應用程式來說,其本身的 System.Windowns.Forms、System.Web 或一般Web Form控制項的複本並不合理。
在 .NET 中,在應用程式之間共用程式碼是明確的決策。 共用的元件有一些額外的需求。 具體來說,共用元件應該同時支援,以便在同一部電腦上安裝及執行相同元件的多個版本,甚至是在同一個程式內。 此外,共用元件具有更嚴格的命名需求。 例如,共用的元件必須具有全域唯一的名稱。
隔離和共用的需求可讓我們考慮兩種「種類」的元件。 這是相當鬆散的分類,因為兩者之間沒有任何實際的結構差異,但差異在於其使用方式:私用至一個應用程式,或在許多應用程式之間共用。
Application-Private元件
應用程式私用元件是只有一個應用程式可見的元件。 我們預期這是 .NET 中最常見的案例。 私人元件的命名需求很簡單:元件名稱在應用程式內必須是唯一的。 不需要全域唯一的名稱。 保留唯一的名稱不是問題,因為應用程式開發人員可以完全控制哪些元件與應用程式隔離。
應用程式私人元件會部署在所使用的應用程式的目錄結構內。 私人元件可以直接放在應用程式目錄中,也可以放在其子目錄中。 CLR 會透過稱為探 查的程式尋找這些元件。 探查只是元件名稱與包含資訊清單之檔案名的對應。
具體來說,CLR 會取得在元件參考中記錄的元件名稱、附加 「.dll」,並在應用程式目錄中尋找該檔案。 此配置上有一些變體,執行時間會在元件所命名的子目錄中,或在元件文化特性所命名的子目錄中查看。 例如,開發人員可以選擇在名為 「de」 的子目錄中部署包含當地語系化為德文之資源的元件,並在名為 「es」 的目錄中部署西班牙文, (如需詳細資訊,請參閱.NET Framework SDK 指南以取得詳細資料。)
如前所述,每個元件資訊清單都包含其相依性的版本資訊。 此版本資訊不會針對私人元件強制執行,因為開發人員完全控制部署至應用程式目錄的元件。
共用組件
.NET Framework也支援共用元件的概念。 共用元件是電腦上多個應用程式所使用的元件。 使用 .NET 時,在應用程式之間共用程式碼是明確的決策。 共用元件有一些額外的需求,旨在避免現今遇到的共用問題。 除了稍早同時描述的支援之外,共用元件還有更嚴格的命名需求。 例如,共用元件必須具有全域唯一的名稱。 此外,系統必須提供「保護名稱」,也就是防止某人重複使用另一個元件名稱。 例如,假設您是方格控制項的廠商,而且您已發行第 1 版的元件。 身為作者,您需要保證其他人無法釋放宣告為第 2 版或格線控制項的元件。 .NET Framework透過名為強式名稱的技術支援這些命名需求, (下一節) 中所述。
一般而言,應用程式作者對應用程式所使用的共用元件沒有相同的控制權。 因此,系統會在每個共用元件的參考上檢查版本資訊。 此外,.NET Framework可讓應用程式和系統管理員藉由指定版本原則來覆寫應用程式所使用的元件版本。
共用元件不一定私下部署至一個應用程式,雖然這種方法仍然可行,特別是在 xcopy 部署是需求時。 除了私人應用程式目錄之外,只要描述元件位置的程式碼基底是在應用程式的組態檔中提供,共用元件也可以部署到全域組件快取或任何 URL。 全域組件快取是多個應用程式所使用的元件全機器存放區。 如前所述,部署至快取並非必要專案,但有一些優點可以這麼做。 例如,會自動提供多個元件的並存儲存體。 此外,系統管理員可以使用存放區來部署錯誤修正或安全性修補程式,讓機器上的每個應用程式都能使用。 最後,有一些與部署至全域組件快取相關聯的效能改善。 第一個涉及驗證強式名稱簽章,如下列強名稱一節所述。 第二個效能改進涉及工作集。 如果數個應用程式同時使用相同的元件,則從磁片上的相同位置載入該元件會利用 OS 所提供的程式碼共用行為。 相反地,從多個不同位置載入相同的元件, (應用程式目錄) 會導致載入相同程式碼的許多複本。 將元件新增至使用者電腦上的快取,通常是使用以 Windows Installer 或其他安裝技術為基礎的安裝程式來完成。 元件永遠不會在快取中成為執行某些應用程式或流覽至網頁的副作用。 相反地,將元件安裝至快取需要使用者一部分的明確動作。 隨附于 Windows XP 和 Visual Studio .NET 的 Windows Installer 2.0 已增強,可完整瞭解元件、元件快取和隔離應用程式的概念。 這表示您將能夠搭配 .NET 應用程式使用所有 Windows Installer 功能,例如隨選安裝和應用程式修復。
每次您想要在開發和測試電腦上將元件新增至快取時,建置安裝套件通常並不實用。 因此,.NET SDK 包含一些工具,可用來處理常式集緩存。 第一個是稱為 gacutil 的工具,可讓您將元件新增至快取,並在稍後移除它們。 使用 /i 參數將元件新增至快取:
gacutil /i:myassembly.dll
See the .NET Framework SDK documentation for a full description of the
options supported by gacutil.
其他工具是 Windows Shell 擴充功能,可讓您使用 Windows 檔案總管和 .NET Framework 組態工具操作快取。 您可以流覽至 Windows 目錄下的 「元件」子目錄來存取 Shell 延伸模組。 您可以在主控台的 [系統管理工具] 區段中找到.NET Framework組態工具。
圖 4 顯示使用殼層延伸模組之全域組件快取的檢視。
圖 4. 全域組件快取
強式名稱
強式名稱可用來啟用與共享元件相關聯的更嚴格命名需求。 強式名稱有三個目標:
- 名稱唯一性。 共用元件必須具有全域唯一的名稱。
- 防止名稱詐騙。 開發人員不希望其他人不小心或刻意釋出您其中一個元件的後續版本,並誤宣告它來自您。
- 提供參考的身分識別。 解析元件的參考時,強式名稱是用來保證載入的元件來自預期的發行者。
強式名稱是使用標準公開金鑰密碼編譯來實作。 一般而言,此程式的運作方式如下:元件的作者會產生金鑰組 (或使用現有的金鑰組) 、使用私密金鑰簽署包含資訊清單的檔案,並將公開金鑰提供給呼叫者使用。 對元件進行參考時,呼叫端會記錄對應至用來產生強式名稱之私密金鑰的公開金鑰。 圖 5 概述此程式在開發階段的運作方式,包括金鑰儲存在中繼資料中的方式,以及簽章的產生方式。
案例是稱為 「Main」 的元件,參考名為 「MyLib」 的元件。MyLib 有共用名稱稱。 重要步驟如下所述。
圖 5. 實作共用名稱稱的程式
開發人員會叫用編譯器,以傳入金鑰組和元件的一組原始程式檔。 金鑰組是使用名為 SN 的 SDK 工具所產生。 例如,下列命令會產生新的金鑰組,並將它儲存至檔案:
Sn –k MyKey.snk The key pair is passed to the compiler using the custom attribute System.Reflection.AssemblyKeyFileAttribute as follows: <assembly:AssemblyKeyFileAttribute("TestKey.snk")>
當編譯器發出元件時,公開金鑰會記錄在資訊清單中,做為元件身分識別的一部分。 將公開金鑰納入身分識別的一部分,就是為元件提供全域唯一的名稱。
發出元件之後,包含資訊清單的檔案會以私密金鑰簽署。 產生的簽章會儲存在 檔案中。
當編譯器產生 Main 時,MyLib 的公開金鑰會儲存在 Main 的資訊清單中,作為 MyLib 參考的一部分。
在執行時間,.NET Framework採取兩個步驟,以確保強式名稱為開發人員提供所需的優點。 首先,只有在元件安裝到全域組件快取時,才會驗證 MyLib 的強式名稱簽章—當應用程式載入檔案時,不會再次驗證簽章。 如果共用元件未部署到全域組件快取,每次載入檔案時都會驗證簽章。 確認簽章可確保在建置元件之後,MyLib 的內容尚未變更。 第二個步驟是確認儲存為 Main 對 MyLib 參考的公開金鑰符合屬於 MyLib 身分識別的公開金鑰。 如果這些金鑰相同,Main 的作者可以確定已載入的 MyLib 版本來自撰寫 Main 所建置之 MyLib 版本的相同發行者。 當從 Main 到 MyLib 的參考解析時,會在執行時間完成此索引鍵等價檢查。
「簽署」一詞通常讓 Microsoft Authenticode® 牢記在心。 請務必瞭解強式名稱與 Authenticode 不會以任何方式相關。 這兩種技術有不同的目標。 特別是,Authenticode 表示與發行者相關聯的信任層級,而強式名稱則不會。 沒有與強式名稱相關聯的憑證或協力廠商封緘授權單位。 此外,強式名稱簽署通常是由編譯器本身做為建置程式的一部分來完成。
另一個值得注意的考慮是「延遲簽署」程式。 元件作者通常無法存取執行完整簽署所需的私密金鑰。 大部分的公司都會將這些金鑰保留在受保護的存放區中,只有少數人才能存取。 因此,.NET Framework提供稱為「延遲簽署」的技術,可讓開發人員只建置具有公開金鑰的元件。在此模式中,不會實際簽署檔案,因為未提供私密金鑰。 相反地,檔案稍後會使用 SN 公用程式簽署。 如需如何使用延遲簽署的詳細資訊,請參閱 .NET Framework SDK 中的延遲簽署元件。
版本原則
如先前所述,每個元件資訊清單都會記錄其所建置之每個相依性版本的相關資訊。 不過,在某些情況下,應用程式作者或系統管理員可能會想要在執行時間以不同版本的相依性執行。 例如,系統管理員應該能夠部署錯誤修正版本,而不需要重新編譯每個應用程式才能挑選修正程式。 此外,如果發現安全性漏洞或其他嚴重 Bug,系統管理員必須能夠指定永遠不會使用特定版本的元件。 .NET Framework透過版本原則啟用版本系結的彈性。
元件版本號碼
每個元件都有四部分版本號碼作為其身分識別 (的一部分,也就是說,某些元件的 1.0.0.0.0 版和 2.1.0.2 版與類別載入器) 完全不同。 為了並存的目的,在身分識別中包含版本是區分元件的不同版本不可或缺的。
版本號碼的部分是主要、次要、組建和修訂。 版本號碼部分沒有套用語意。 也就是說,CLR 不會根據指派版本號碼來推斷元件的相容性或任何其他特性。 身為開發人員,您可以視需要變更此數位的任何部分。 雖然沒有套用至版本號碼格式的語意,但個別組織可能會發現建立版本號碼變更方式的慣例很有用。 這有助於維護整個組織的一致性,並更輕鬆地判斷建置特定元件的來源專案。 一個典型的慣例如下:
主要或次要。 版本號碼主要或次要部分的變更表示不相容的變更。 在此慣例下,2.0.0.0 版會被視為與 1.0.0.0 版不相容。 不相容變更的範例是變更某些方法參數的類型,或完全移除類型或方法。
組建。 組建編號通常用來區分每日組建或較小的相容版本。
修訂。 修訂編號的變更通常會保留給修正特定 Bug 所需的累加組建。 您有時會聽到這稱為「緊急錯誤修正」號碼,因為修訂是特定 Bug 的修正寄送給客戶時通常會變更的內容。
預設版本原則
解析共用元件的參考時,CLR 會決定當相依性在程式碼中跨越該元件的參考時要載入的相依性版本。 .NET 中的預設版本原則非常簡單。 解析參考時,CLR 會從呼叫元件的資訊清單取得版本,並使用完全相同的版本號碼載入相依性版本。 如此一來,呼叫端就會取得他所建置和測試的確切版本。 此預設原則具有 屬性,可保護應用程式免于不同的應用程式安裝現有應用程式相依之新版共用元件的案例。 回想一下,在 .NET 之前,現有的應用程式預設會開始使用新的共用元件。 不過,在 .NET 中,新版共用元件的安裝不會影響現有的應用程式。
自訂版本原則
有時候,系結至應用程式隨附的確切版本不是您想要的版本。 例如,系統管理員可能會將重大錯誤修正部署到共用元件,並想要讓所有應用程式使用這個新版本,而不論其建置的版本為何。 此外,共用元件的廠商可能已將服務版本運送至現有的元件,而且希望所有應用程式都開始使用服務版本,而不是原始版本。 透過版本原則.NET Framework支援這些案例和其他案例。
版本原則會在 XML 檔案中陳述,只是載入某個元件版本的要求,而不是另一個版本。 例如,下列版本原則會指示 CLR 載入 5.0.0.1 版,而不是名為一個元件之 5.0.0.0.0 版:
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly
<assemblyIdentity name="MarineCtrl" publicKeyToken="9335a2124541cfb9" />
<bindingRedirect oldVersion="5.0.0.0" newVersion="5.0.0.1" />
</dependentAssembly>
</assemblyBinding>
除了從特定版本號碼重新導向至另一個版本號碼之外,您也可以從某個版本範圍重新導向至另一個版本。 例如,下列原則會將 0.0.0.0 到 5.0.0.0 版的所有版本重新導向至 5.0.0.1 版:
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly
<assemblyIdentity name="MarineCtrl" publicKeyToken="9335a2124541cfb9" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.1" />
</dependentAssembly>
</assemblyBinding>
版本原則層級
.NET 中可以套用版本原則的三個層級:應用程式特定原則、發行者原則和全電腦原則。
應用程式特定原則。 每個應用程式都有選擇性的組態檔,可指定應用程式想要系結至不同版本的相依元件。 組態檔的名稱會根據應用程式類型而有所不同。 對於可執行檔,組態檔的名稱是可執行檔的名稱 + 「.config」 副檔名。 例如,「myapp.exe」 的組態檔會是 「myapp.exe.config」。 ASP.NET 應用程式的組態檔一律為「web.config」。
發行者原則。 雖然應用程式特定原則是由應用程式開發人員或系統管理員所設定,但發行者原則是由共用元件的廠商所設定。 發行者原則是廠商有關其元件不同版本的相容性聲明。 例如,假設共用Windows Forms控制項的廠商隨附服務版本,其中包含控制項的一些錯誤修正。 原始控制項是 2.0.0.0 版,而服務版本的版本是 2.0.0.1。 因為新版本只包含錯誤修正, (控制廠商可能會發行發行者原則,) 讓使用 2.0.0.0.0 的現有應用程式開始使用 2.0.0.1 的現有應用程式,所以控制廠商可能會發出任何重大 API 變更。 發行者原則是以 XML 表示,就像應用程式和全電腦原則一樣,但與其他原則層級不同,發行者原則會以元件本身的形式散發。 這是因為確保發行特定元件之原則的組織是發行元件本身的相同組織。 這可藉由要求原始元件和原則元件都提供具有相同索引鍵組的強式名稱來完成。
全機器原則。 最後的原則層級是全機器原則 (有時稱為系統管理員原則) 。 全電腦原則會儲存在位於 .NET Framework 安裝目錄下的 「config」 子目錄中的 machine.config。 安裝目錄為 %windir%\microsoft.net\framework\%runtimeversion%。 machine.config中所做的原則聲明會影響電腦上執行的所有應用程式。 系統管理員會使用全電腦原則來強制指定電腦上的所有應用程式使用特定版本的元件。 最常使用的批註案例是當安全性或其他重大錯誤修正已部署到全域組件快取時。 部署固定元件之後,系統管理員會使用全電腦版本原則,以確保應用程式不會使用舊版中斷的元件。
原則評估
CLR 在系結至強式名稱元件時,第一件事是決定要系結的元件版本。 此程式會從讀取所記錄之元件資訊清單中所記錄之所需元件的版本號碼開始進行參考。 接著會評估原則,以判斷任何原則層級是否包含重新導向至不同版本。 原則層級會依序評估,從應用程式原則開始,後面接著發行者和系統管理員。
在任何層級找到的重新導向會覆寫上一個層級所做的任何語句。 例如,假設元件 A 參考元件 B。A 資訊清單中 B 的參考是 1.0.0.0 版。 此外,元件 B 隨附的發行者原則會將參考從 1.0.0.0 重新導向至 2.0.0.0。 此外,還有一個版本原則是整個電腦的組態檔,會將參考導向至 3.0.0.0 版。 在此情況下,在機器層級所做的語句將會覆寫在發行者層級所做的語句。
略過發行者原則
由於套用三種原則類型的順序,發行者的版本重新導向 (發行者原則) 可以覆寫呼叫元件中繼資料中記錄的版本,以及已設定的任何應用程式特定原則。 不過,強制應用程式一律接受發行者對於版本設定的建議,可能會導致 DLL 地獄。 最後,DLL Hell 主要是因為在共用元件中維護回溯相容性的困難所造成。 為了避免應用程式因安裝新版本共用元件而中斷的情況,.NET 中的版本原則系統可讓個別應用程式略過發行者原則。 換句話說,應用程式可以拒絕接受發行者對於要使用哪個版本的建議。 應用程式可以使用應用程式組態檔中的 「publisherPolicy」 元素略過發行者原則:
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<publisherPolicy apply="no"/>
</assemblyBinding>
使用 .NET 組態工具設定版本原則
幸運的是,.NET Framework隨附圖形管理工具,因此您不必擔心手動編輯 XML 原則檔案。 此工具同時支援應用程式和全機器版本原則。 您可以在 主控台 的 [系統管理工具] 區段中找到此工具。 管理工具的初始畫面看起來像圖 6:
圖 6. 管理員工具
下列步驟說明如何設定應用程式特定的原則:
將您的應用程式新增至樹狀檢視中的 [應用程式] 節點。 以滑鼠右鍵按一下 [應用程式] 節點,然後按一下 [ 新增]。 [ 新增 ] 對話方塊會顯示要從中挑選的 .NET 應用程式清單。 如果您的應用程式不在清單中,您可以按一下 [其他] 加以新增。
選擇您想要設定原則的元件。 以滑鼠右鍵按一下 [已設定的元件] 節點,然後按一下 [ 新增]。 其中一個選項是從應用程式參考的元件清單中挑選元件,並顯示下列對話方塊,如圖 7 所示。 挑選元件,然後按一下 [ 選取]。
圖 7. 選擇元件
在 [ 屬性 ] 對話方塊中,輸入版本原則資訊。 按一下 [ 系結原則 ] 索引標籤,然後在資料表中輸入所需的版本號碼,如圖 8 所示。
圖 8. 系結原則索引標籤
部署
部署至少牽涉到兩個不同的層面:封裝程式碼,並將封裝散發至應用程式執行所在的各種用戶端和伺服器。 .NET Framework的主要目標是簡化部署 (特別是散發層面) ,方法是讓零影響安裝和 xcopy 部署可行。 元件的自我描述本質可讓我們移除對登錄的相依性,進而讓安裝、卸載和複寫變得更簡單。 不過,在某些情況下,xcopy 不足以或適當作為散發機制。 在這些情況下,.NET Framework提供廣泛的程式碼下載服務,並與 Windows Installer 整合。
包裝
第一版.NET Framework有三個可用的封裝選項:
- 建置 (DLL 和 EXE) 。 在許多情況下,不需要特殊封裝。 應用程式可以使用開發工具所產生的格式進行部署。 也就是說,DLL 和 EXE 的集合。
- Cab 檔案。 Cab 檔案可用來壓縮您的應用程式,以取得更有效率的下載。
- Windows Installer 套件。 Microsoft Visual Studio .NET 和其他安裝工具可讓您建置 Windows Installer 套件 (.msi 檔案) 。 Windows Installer 可讓您利用應用程式修復、隨選安裝和其他 Microsoft Windows 應用程式管理功能。
散發案例
.NET 應用程式可以透過各種方式散發,包括 xcopy、程式碼下載,以及透過 Windows Installer 散發。
對於許多應用程式,包括 Web 應用程式和 Web 服務,部署就像將一組檔案複製到磁片一樣簡單,並加以執行。 卸載和複寫很簡單,只要刪除檔案或複製這些檔案即可。
.NET Framework使用網頁瀏覽器提供廣泛的程式碼下載支援。 在此領域中已進行數項改善,包括:
- 零影響。 電腦上不會進行任何登錄專案。
- 累加式下載。 元件片段只會在參考時下載。
- 下載隔離至應用程式。 代表一個應用程式下載的程式碼不會影響電腦上的其他人。 程式碼下載支援的主要目標是防止使用者下載新版本的共用元件,作為流覽至特定網站的副作用,並讓該新版本對其他應用程式造成負面影響的案例。
- 沒有 Authenticode 對話方塊。 程式碼存取安全性系統可用來允許行動程式碼以部分信任層級執行。 使用者永遠不會看到對話方塊,要求他們決定是否信任程式碼。
最後,.NET 已與 Windows Installer 和 Windows 的應用程式管理功能完全整合。
總結
.NET Framework啟用零影響安裝和位址 DLL Hell。 元件是用來啟用這些功能的自我描述、可設定版本部署單位。
建立隔離應用程式的能力很重要,因為它可讓應用程式受到其他應用程式對系統所做的變更所影響。 .NET Framework透過應用程式目錄結構內部署的應用程式私用元件,鼓勵這種類型的應用程式。
並存是 .NET 中共用和版本控制案例的核心部分。 並排允許同時在電腦上安裝和執行元件的多個版本,並允許每個應用程式要求該元件的特定版本。
CLR 會在應用程式片段之間記錄版本資訊,並在執行時間使用該資訊,以確保載入適當的相依性版本。 應用程式開發人員和系統管理員都可以使用版本原則,在選擇載入指定元件的哪個版本時提供一些彈性。
.NET Framework提供數個封裝和散發選項,包括 Windows Installer、程式碼下載和簡單的 xcopy。