【轉】使用 NuGet 管理專案庫-Phil Haack

eedc發表於2016-11-27

原文地址:https://msdn.microsoft.com/zh-cn/magazine/hh547106.aspx

 

無論多麼努力,Microsoft 也沒辦法提供開發人員所需要的每一個庫。 雖然 Microsoft 在全球的員工人數接近 90,000,但全球的開發人員數以百萬計。 指望 Microsoft 滿足每一個人的需求是不現實的,也不可想像。因此,開發人員通常得自己動手解決問題,他們目前已經編寫了成千上萬的實用庫,並將其釋出到 Web 上。

如何共享如此多的庫是一個令人頭痛的問題。 共享和重用程式碼是一個很大的挑戰。 不相信? 請隨便走進一間中型或大型工作室,問問他們有多少日誌記錄庫。 訪問多家公司後,您將發現他們擁有比例非常高的內部日誌記錄庫,而這些庫中有一些非常不錯,例如,Log4Net、NLog 和 Error Logging Modules and Handlers(即 ELMAH)。

當一位開發人員開始新專案時,他將面對一張空白的畫布。 他如何去發現這些有用的庫? 如何將庫整合到當前專案中並管理庫的依賴項和更新呢?

ELMAH 就是一個非常有用的庫,是由開發人員自己編寫的。 ELMAH 能夠在出現異常時記錄 Web 應用程式中所有未經處理的異常以及所有請求資訊,例如,標頭、伺服器變數等。 假設您剛剛聽說 ELMAH 並希望在下一個專案中使用它。

您可能會採取下列步驟:

  1. 查詢 ELMAH。由於它名稱獨特,Bing 搜尋的第一條搜尋結果將是 ELMAH Google 內碼表。
  2. 下載正確的 zip 包。該站點的下載頁面有多個 zip 包。 您必須思考並選取正確的一個。 有時,您並不能一眼就看出正確的是哪個。
  3. “取消阻止”程式包。從 Web 下載程式包後,您需要右鍵單擊該檔案,開啟“屬性”對話方塊,然後單擊“取消阻止”按鈕以從該檔案刪除“Web 的標記”。
  4. 驗證其雜湊值是否與託管環境提供的雜湊值相符。Google 程式碼站點會顯示代表該 zip 檔案的 QR 程式碼。在您認識的開發人員中,有多少會抽出時間來根據 QR 程式碼驗證檔案?
  5. 將程式包的內容解壓縮到解決方案中的特定位置。大多數開發人員會避免將程式集解壓縮到 bin 目錄,這是因為該目錄用於生成輸出而非輸入,並且不在版本控制的跟蹤範圍之內。 實際上,有必要將該依賴項新增到版本控制之下的資料夾,並從該位置引用該程式集。
  6. 在專案中新增程式集引用。必須在 Visual Studio 專案中新增對該程式集的引用,然後才能使用該程式集。
  7. 使用正確的設定更新 web.config。這可能意味著您要使用 Bing 或 Google 進行更多搜尋才能找到配置檔案所需的正確設定。

真是很麻煩! 現在,假設您必須為 10 至 15 個依賴項執行這些操作。 當您的應用程式要釋出新版本時,您需要花費大量時間為應用程式的依賴項搜尋更新。

“非我發明”(NIH) 過去常常遭到非議,而在現在聽起來卻是不錯的主意。

NuGet 應運而生

NuGet 是一種 Visual Studio 擴充套件,它能夠簡化在 Visual Studio 專案中新增、更新和刪除庫(部署為程式包)的操作。 NuGet 程式包是打包成一個檔案的檔案集,副檔名是 . nupkg,使用開放打包約定 (OPC) 格式。

OPC 僅僅是具有某些後設資料的 zip 檔案的首字母縮寫詞。 事實上,您可能早已熟悉 OPC,因為 Word 和 Excel 文件正是使用此格式。 如果您取一個 .docx 檔案並將副檔名改為 .zip,您實際可以開啟它並瀏覽裡面的內容。 nupkg 檔案同樣如此。

NuGet 產品同樣隨附能夠輕鬆建立和釋出程式包的實用工具。 現在,我先重點介紹如何使用 NuGet 發現和安裝程式包。 之後,我將講述如何建立和釋出程式包。

安裝 NuGet

要安裝 NuGet,從“Tools”(工具)|“Extension Manager”(擴充套件管理器)選單選項啟動 Visual Studio Extension Manager。 單擊“Online Gallery”(聯機庫)選項卡檢視可用的 Visual Studio 副檔名,如圖 1中所示。 如您所見,NuGet 位於第一個螢幕,是排名最高的程式包。 如果情況不是如此,您可以使用右上角的搜尋框找到它。 單擊“Download”(下載)按鈕安裝 NuGet。

 
圖 1:Visual Studio Extension Manager

如果您已經安裝了 ASP.NET MVC 3,則您已經安裝 NuGet。 ASP.NET MVC 3 包含 NuGet,並且 Microsoft 計劃在下一版本的 Visual Studio 中包含 NuGet。

安裝程式包

我們啟動使用者友好的 NuGet 對話方塊以安裝程式包。 NuGet 同樣內建基於 Windows PowerShell 的控制檯,此控制檯面向高階使用者(將在下文提及)。

要啟動 NuGet,請右鍵單擊專案的引用節點,然後選擇“Manage NuGet Packages”(管理 NuGet 程式包)選項(NuGet 1.4 之前的該選項具有不同的標籤)。 這將啟動“Manage NuGet Packages”(管理 NuGet 程式包)對話方塊,如圖 2 中所示。

 
圖 2:“NuGet Package Manager”(NuGet 程式包管理器)對話方塊

請確保選中“Online”(聯機)選項卡,並在右上角輸入搜尋詞(例如,搜尋來自 StackOverflow.com 的實用庫 MiniProfiler)。

找到程式包後,單擊“Install”(安裝)按鈕安裝該程式包。 NuGet 隨後下載該程式包和它的依賴項,並將任何必要更改應用到程式包指定的專案中。

NuGet 執行下列步驟安裝程式包:

  1. 下載程式包檔案及其所有依賴項。有些程式包要求顯式接受許可,並提示使用者接受程式包的許可條款。 大多數程式包支援隱式接受許可,並不發出提示。 如果解決方案或本地計算機快取中已經存在該程式包,NuGet 將跳過下載程式包的步驟。
  2. 提取程式包的內容。NuGet 將內容提取到程式包資料夾中(在必要時建立資料夾)。 程式包資料夾在您的解決方案 (.sln) 檔案的並列位置。 如果解決方案的多個專案中安裝了同一個程式包,則僅提取該程式包一次並由各專案共享。
  3. 引用程式包中的程式集。依照慣例,NuGet 更新專案以引用程式包 lib 資料夾中的一個或多個特定程式集。 例如,將程式包安裝到面向 Microsoft .NET Framework 4 的專案時,NuGet 將新增對 lib/net40 資料夾中的程式集的引用。
  4. 將內容複製到專案中。依照慣例,NuGet 將程式包的內容資料夾的內容複製到專案中。 這對包含 JavaScript 檔案或影象的程式包十分有用。
  5. 應用程式包轉換。如果任何程式包包含轉換檔案,例如用於配置的 app.config.transform 或 web.config.transform,則 NuGet 將在複製內容之前應用這些轉換。 有些程式包所含的原始碼經過轉換可以在原始檔中包含當前專案的名稱空間。 NuGet 同樣轉換這些檔案。
  6. 執行程式包中關聯的 Windows PowerShell 指令碼。有些程式包可能包含 Windows PowerShell 指令碼,這些指令碼使用設計時環境 (DTE) 自動化 Visual Studio,從而處理與 NuGet 無關的任務。

當 NuGet 執行所有這些步驟後,庫將準備就緒。 很多程式包使用 WebActivator 程式包自行啟用,從而最小化安裝後所需的任何配置。

您可以解除安裝程式包,這將使專案回到安裝程式包之前的狀態。

更新程式包

在《軟體工程的事實和謬誤》(Addison-Wesley Professional,2002 年)一書中,Robert L. Glass 說:“維護工作通常約佔軟體成本的 40-80%(平均是 60%)。 因此,維護可能是軟體生命週期中最重要的階段。”

安裝程式包僅僅是故事的開始。 在今後維護這些庫時,您必須及時更新應用程式,為庫安裝最新的 Bug 修補程式。 這需要您回答一個問題:“此專案中的哪些依賴項有可用的最新更新?”

如前所述,回答此問題一直是一項耗時的工作。 然而,有了 NuGet,您只需啟動對話方塊並單擊“Updates”(更新)選項卡即可看到有可用更新的程式包列表,如圖 3 所示。 單擊“Update”(更新)按鈕可將程式包更新到最新版本。

 
圖 3 當前專案的可用更新

更新命令解除安裝舊程式包,然後安裝新程式包,確保所有依賴項都根據需要得到更新。

NuGet 在程式包管理器控制檯中提供便於控制更新的命令,例如,用以更新解決方案中的所有程式包或執行“安全”更新。

面向高階使用者的 NuGet

雖然我對美觀的 GUI 對話方塊非常痴迷,但我知道很多開發人員非常鄙視像我一樣的滑鼠操作者。 這些開發人員將命令列 shell 視作 UI,他們更願意通過這些 shell 組成命令集。

NuGet 能夠滿足這種需求,它提供基於 Windows PowerShell 的控制檯視窗(稱作程式包管理器控制檯)以及一組 Windows PowerShell 命令與 NuGet 進行互動。 Windows PowerShell 是基於 .NET 的指令碼語言和命令列 shell,非常適合組成命令集,並能夠處理物件。

要啟動程式包管理器控制檯,請導航至“Tools”(工具)|“Library Package Manager”(庫程式包管理器)|“Package Manager Console”(程式包管理器控制檯)選單選項。

列出和安裝程式包

要列出和搜尋程式包,請使用 Get-Package 命令。 預設情況下,該命令列出當前專案中的已安裝程式包。 您可以通過指定 ListAvailable 標記和 Filter 標記聯機搜尋程式包。 下列命令列搜尋所有包含“MVC”的程式包:

Get-Package -ListAvailable -Filter Mvc

找到要安裝的程式包後,使用 Install-Package 命令。 例如,要將 ELMAH 安裝到當前專案:

Install-Package Elmah

由於 Windows PowerShell 是動態語言,它能夠提供 Tab 擴充套件功能,從而幫助您正確輸入命令列引數。 Tab 擴充套件等同於 C# 的 IntelliSense,但與基於編譯時資訊的 IntelliSense 不同,您可以在執行時填充 Tab 擴充套件。

例如,如果您輸入 Install-Package Mvc{tab},您將看見一個列表,包含可能來自程式包源的程式包名稱,如圖 4 所示。

 
圖 4:Tab 擴充套件的程式包列表

更新程式包

程式包管理器控制檯還包含一個命令,與對話方塊相比,它提供更多的更新控制。 例如,無需引數即可呼叫此命令以更新解決方案的每個專案中的各程式包:

Update-Package

此命令嘗試將每個程式包都更新到最新版本。 因此,如果您有 1.0 版本的程式包,而 1.1 和 2.0 版本在該程式包源中可用,則該命令將此程式包更新至最新的 2.0 版本。

如果任何程式包包含重大改變,這會是一項非常重大的操作。 在多數情況下,您僅希望將各程式包更新至最新的修補程式版本。 這叫“安全”更新,前提是具有較大內部版本號或修訂號(但具有相同的主版本號和次版本號)的程式包能夠向後相容。 僅新增 Safe 標記以執行安全更新,例如:

Update-Package -Safe

在這種情況下,如果您安裝了 1.0.0 版本的程式包,而 1.0.1 和 1.1 版本在該程式包源中可用,則該程式包將安全地升級至 1.0.1 而非 1.1。

Update-Package 命令還提供更精細的控制,例如,將程式包更新至特定版本而非最新版本。

用新命令擴充套件 Visual Studio

雖然使用 Windows PowerShell 安裝程式包的功能很不錯,但這不是我們選擇 Windows PowerShell 的最主要原因。 最主要原因之一是程式包能夠將新命令新增至程式包管理器控制檯。 這些命令能夠與 Visual Studio DTE 互動以執行各種任務。

例如,安裝 MvcScaffolding 程式包時,它會將新 Scaffold Controller 命令新增至控制檯。 給定一個 Entity Framework (EF) Code First 物件,此命令生成一個控制器、控制器操作,以及 EF 物件的基本建立、讀取、更新和刪除 (CRUD) 操作對應的檢視,如圖 5 所示。

 
圖 5:執行中的 MvcScaffolding Custom Scaffold 命令

您的組織中的 NuGet

由於使用者僅關注 NuGet 如何簡化與公共開發人員社群共享庫,使用者通常很容易忽視 NuGet 在企業中的作用。

畢竟,企業也沒有特殊手段能夠避免整個社群所面臨的程式碼共享難題。 隨著公司的成長,平均資訊量也在增加。 在同一間公司,不同的組使用各自專有版本的公司“標準”庫。 有些組可能會完全無視這些庫,而是從頭自己編寫。

問題往往不在於庫本身,而在於與其他團隊共享這些庫並通知他們更改時的麻煩。 聽起來是不是很熟悉?

程式包來源

至此,我已講完如何安裝程式包,但尚未回答這樣一個明顯的問題:這些程式包在哪? 它們位於 nuget.org 的官方 NuGet 程式包庫中。 此庫公開了一個 OData 源:packages. nuget.org/v1/FeedService.svc。

OData 格式使 NuGet 客戶端能夠在客戶端上生成搜尋程式包源的特定查詢,但是會在伺服器上執行這些查詢。

要向 NuGet 新增更多程式包源,請導航至“Tools”(工具)|“Library Package Manager”(庫程式包管理器)|“Package Manager Settings”(程式包管理器設定)選單選項,單擊“Package Sources”(程式包源)節點,如圖 6 所示。

 
圖 6:程式包管理器設定

預設的程式包源位於 Web 上的 OData 端點,但示例螢幕快照同樣將本地資料夾顯示為程式包源。 NuGet 將資料夾(無論位於本地還是網路共享位置)視為程式包源,並在“Online”(聯機)窗格中列出資料夾中的每個程式包。 這樣一來,只需將程式碼放入一個資料夾即可與他人共享,並且在測試您自己建立的程式包時同樣有用。

託管您自己的 NuGet 伺服器

除了在網路共享上託管程式包之外,您還可以將網站設定為程式包源,使用網站與組織中的其他人共享程式包。

如果有很多工,還有一個程式包可以在此處幫到您。 首先,在 Visual Studio 中建立一個空的 ASP.NET Web 應用程式(面向 ASP.NET 4)。 使用 NuGet 安裝程式包 NuGet.Server。 此程式包將簡單的 OData 端點新增到空 Web 應用程式中。

接著,將程式包檔案新增到 Web 應用程式的 Packages 資料夾,以便釋出它們並部署網站。 有關如何設定的詳細資訊,請參閱 NuGet 的文件站點 bit.ly/jirmLO。

如果您希望部署類似 nuget.org 的完整庫體驗,NuGet 庫程式碼還可通過 nugetgallery.codeplex.com 專案作為開放源專案提供。

通過託管專用 NuGet 伺服器或庫實施,您可以方便地在公司內部共享專有程式碼,無需公開發布。

建立程式包

NuGet 發揮作用的前提是有程式包可供安裝。 NuGet 中的有用程式包越多,NuGet 對每一位開發人員的價值越大。 這就是 NuGet 團隊不辭勞苦儘可能建立使用方便的程式包的原因。 雖然程式包的建立不難,但 NuGet 團隊一直在對其功能進行投資,以使之更加簡單。 他們已開發若干用於建立 NuGet 程式包的工具。例如,Package Explorer 是 NuGet 團隊的開發人員編寫的一個 ClickOnce 應用程式,使用者可以憑藉它在建立或測試程式包時進行視覺化操作。 您可以在 npe.codeplex.com 下載該應用程式。

NuGet.exe

由於大多數程式包作者都希望將程式包的建立整合到生成流程,讓我們看看使用 NuGet 命令列實用工具的其他方法。 您僅需從 bit.ly/gmw54b 下載一次該實用工具。 下載 NuGet.exe 後,請確保將其放入已新增至 PATH 環境變數的資料夾。 我針對此類實用工具建立了一個名為“utils”的資料夾。

我之所以說只需下載 NuGet.exe once 一次(每臺計算機一次),是因為它是自行更新的可執行程式。 如果出現更新的版本,僅需執行以下命令,NuGet 即會聯機檢查並自行更新至最新版本:

nuget update –self

該命令列工具能夠查詢類似程式包管理器控制檯的聯機源。 例如,要搜尋帶“MVC”的所有程式包,可使用以下命令:

nuget list Mvc

NuGet.exe 甚至能夠下載程式包和依賴項並解壓縮它們,但它不能將專案修改為引用已下載的程式包程式集或執行程式包中包含的任何 Windows PowerShell 指令碼。

從專案建立程式包

程式包在 90% 的情況下僅包含一個程式集(據本人統計)。 此部分講述使用 NuGet.exe 針對專案檔案(例如,.csproj 或 .vbproj 檔案)建立上述程式包的簡單流程。

有關建立較複雜程式包(例如,針對不同 .NET Framework 版本的單個程式包)的詳細資訊,請參閱 docs.nuget.org 的 NuGet 文件網站。

建立程式包的基本步驟:

  1. 建立一個類庫專案。
  2. 從專案生成 NuSpec 清單。
  3. 更新專案的程式集後設資料。
  4. 使用 NuGet.exe 建立程式包。

建立類庫專案。要共享程式集,首先要建立類庫專案。 NuGet 可以壓縮多個專案型別,但共享程式碼時最常見的情況是使用類庫。 建立程式包後,務必開啟 AssemblyInfo.cs 檔案以更新程式集的後設資料。

建立 NuSpec 清單。NuSpec 檔案是程式包清單,包含與程式包有關的重要後設資料(例如,作者、描述和程式包依賴項)。 自己動手建立 NuSpec 格式很簡單,但使用 spec 命令建立此檔案更加方便。 確保在專案檔案所在目錄中執行命令:

  nuget spec

在此特定情況下,由於 spec 命令從專案檔案生成 NuSpec,它會包含某些後設資料的佔位符,如圖 7 中所示。

圖 7:生成的 NuSpec 檔案

 
<?xml version="1.0"?>
<package xmlns=
  "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
  <metadata>
    <id>$id$</id>
    <version>$version$</version>
    <title>$title$</title>
    <authors>$author$</authors>
    <owners>$author$</owners>
    <licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl>
    <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl>
    <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>$description$</description>
    <copyright>Copyright 2011</copyright>
    <tags>Tag1 Tag2</tags>
  </metadata>
</package>

請勿編輯包含佔位符的欄位,但應在其他欄位(例如,licenseUrl、projectUrl、iconUrl 和 tags)中填入正確的值。

更新專案的程式集後設資料。每個程式集都有與其關聯的後設資料。 NuGet 能夠讀取這些程式集後設資料並在建立程式包時將其合併到 NuSpec 清單,從而確保了此資訊始終在您的程式包和程式集中保持同步。

如此前所述,此資訊通常位於名為 AssemblyInfo.cs 的檔案中。 圖 8 中的表顯示了程式集後設資料和 NuSpec 佔位符值之間的對應關係。

圖 8:對映到 NuSpec 的程式集後設資料

標記
$id$ 程式集名稱。
$title$ AssemblyTitleAttribute 中指定的程式集標題。
$version$ 程式集的 AssemblyVersionAttribute 中指定的程式集版本。
$author$ AssemblyCompanyAttribute 中指定的公司。
$description$ AssemblyDescriptionAttribute 中指定的描述。

與其他欄位不同,$id$ 欄位並非從程式集屬性提取,而是設定為程式集名稱。

建立程式包。在專案檔案和 NuSpec 檔案所在目錄中,執行以下命令以建立程式包:

nuget pack ProjectName.csproj

如果同一個目錄中只有一個專案檔案,則在執行命令時可以省略專案檔名稱。

如果尚未編譯專案,可先用 Build 標記編譯專案,然後壓縮它。 這將在執行 pack 命令之前編譯專案:

nuget pack ProjectName.csproj -Build

此命令將生成名為 ProjectName.{version}. nupkg 的檔案,其中,{version} 的值與 AssemblyVersionAttribute 中指定的值相同。 例如,如果版本是 1.0.0,您的程式包將命名為 ProjectName.1.0.0. nupkg。 您可以在操作後使用 Package Explorer 檢查程式包,以確保建立正確。

為了方便開發人員安裝您的程式包,請考慮使用 Symbols 標記建立帶偵錯程式符號的程式包:

nuget pack ProjectName.csproj -Build -Symbols

除了主程式包之外,此命令還建立符號程式包。 這使安裝您的程式包的其他人在除錯其應用程式時能夠單步執行程式包程式碼。

釋出程式包

建立程式包後,您可能希望與全世界共享。 NuGet.exe 專門針對此目的提供一條釋出命令。 釋出之前,您需要在 nuget.org 上建立一個帳戶。

註冊帳戶後,單擊指向您的帳戶的連結以檢視您的訪問金鑰。 此金鑰非常重要,因為向 nuget.exe 命令標識庫,並且是可撤消的密碼。

一旦擁有自己的金鑰後,請使用以下命令將其儲存在安全的位置:

nuget setApiKey b688a925-0956-40a0-8327-ff2251cf5f9a

儲存金鑰後,使用 push 命令將您的程式包釋出到庫:

nuget push ProjectName.1.0.0. nupkg

在庫上載程式包之前,該命令將驗證庫的 API 金鑰。 如果您建立了前述符號程式包,則應在對程式包執行 push 命令時指定 Symbols 標記:

nuget push ProjectName.1.0.0. nupkg -Symbols

確保指定主程式包名稱而非符號程式包名稱。 依照慣例,此命令查詢特定的符號程式包。 此命令將主程式包推送到 NuGet 庫,並將符號程式包推送到合作伙伴的 symbolsource.org 儲存庫。

後序

在本文中,我演示了 NuGet 如何從 NuGet 庫提取有用的庫以助推新專案開發的啟動。 在企業內部,NuGet 可用於在組織中的不同開發人員之間共享程式碼。

但是我需要指出,大家對 NuGet 存在一種固有的誤解:NuGet 僅適合 Web 開發人員。 該誤解可能源於它隨附在 ASP.NET MVC 3 版本中,但這種觀點是錯誤的。 NuGet 並非僅針對 Web 開發人員,而是所有開發人員。 NuGet 支援 Windows Phone、Silverlight、Windows Presentation Foundation 以及其他專案型別,並將在今後支援新的專案型別。

NuGet 是社群驅動的開放原始碼專案,通過 Apache 2 許可註冊。 該專案屬於 Outercurve Foundation,但已整合到 Microsoft 的產品,並且若干 Microsoft 開發人員已成為它的核心參與者。

若希望幫助 NuGet 的開發,請訪問 nuget.codeplex.com 以瞭解如何參與其中以及如何對 NuGet 做出貢獻。

本文章僅對 NuGet 的功能進行了初步探討。 要了解更多資訊,請訪問 docs. nuget.org 的 NuGet 文件網站(開發團隊在對其進行精心維護,並接受對其文件的貢獻)。 開發團隊積極參與 CodePlex 討論論壇中有關 NuGet 專案的討論,並歡迎您提出有關問題。

 

Phil Haack 是 Microsoft ASP.NET 團隊的高階專案經理,專注於為開發人員建立優秀的產品。他參與 ASP.NET 很多領域的工作,他的主要專案是 ASP.NET MVC 和 NuGet 程式包管理器,這兩個產品均通過 OSS 許可證釋出。在工作之餘,他會在部落格 (haacked.com) 上寫有關軟體的文章,並研究 Subtext 開放原始碼部落格引擎。

衷心感謝以下技術專家對本文的審閱: David Fowler

相關文章