簡體   English   中英

從 NuGet 包加載程序集

[英]Loading Assemblies from NuGet Packages

有時在我的 PowerShell 腳本中,我需要使用Add-Type -AssemblyName訪問特定的 DLL。 但是,我需要的 DLL 並不總是在機器上或 GAC 中。 例如,我可能想要一個使用 Dapper 查詢數據庫的快速腳本。 在這些情況下,我一直在復制 DLL 和ps1文件。 我想知道這是否常見/一個好主意,以及是否有一個現有的擴展可以加載 NuGet 包,然后將其存儲在全局或本地文件夾中並自動調用Add-Type -AssemblyName

這很像分別在 Node.js 或 Python 中使用npmpip

更新

我做了一些研究,發現舊版本的 PowerShell 沒有內置任何內容。 我在嘗試使用nuget.exe從頭開始編寫一個時取得了一些進展

&"$(Get-Location)/nuget.exe" install $packageName -Version $version -OutputDirectory "$(Get-Location)/packages" -NoCache -NoInteractive

這將下載當前文件夾中“packages”文件夾下的給定包/版本,以及它的任何依賴項。 但是,看起來它會下載每個框架版本,但沒有明顯的方法來告訴您在給定環境中使用哪個版本。

否則,您可以循環遍歷結果並調用 Add-Type:

Get-ChildItem .\packages\ -Recurse -Filter "*.dll" | % {
    try
    {
        Add-Type -Path $_.FullName
    }
    catch [System.Exception]
    {
    }
}

我嘗試通過project.json文件使用restore命令來查看是否可以在沒有運氣的情況下控制框架版本。 這對我來說太hacky了。

我將查看@crownjitter 關於使用 PowerShell 5 的建議。

更新

使用@crownjitter 的建議,我最終能夠使用 NuGet 注冊 PackageManagement 模塊(請參閱下面的評論)。 使用以下命令,我能夠重現上面的Nuget.exe命令正在執行的操作:

Install-Package Dapper -Destination packages

顯然,這要短得多。 問題是它有同樣的限制; 它降低了包的每個框架版本。 如果這包括 .NET 核心,它會降低很多 .NET 核心框架! 似乎沒有辦法指定目標框架(也就是 .NET 4.5.1 或更低版本)。

安裝包抓取所有 .NET 框架的依賴項

我想知道是否有辦法根據 PowerShell 當前的$PSVersionTable.CLRVersion字段確定從哪個 NuGet 包文件夾加載 DLL。

Crownedjitter 的有用答案是一個很好的起點,Travis 本人在評論中提供了額外的指示,但讓我嘗試從 Windows PowerShell v5.1 / PowerShell [Core] 7.1開始總結

更新:下面的原始答案包含一些有用的一般指針,以及指向GitHub 上功能建議的鏈接,以將 NuGet 包與Add-Type集成,但它顯示的基於Install-Package的方法最終達不到要求,因為它沒有考慮包的依賴關系,正如BACON指出的那樣:

缺少的一個(非常重要的)步驟是加載任何可能已安裝的依賴項。 由於Dependencies 屬性不包含足夠的信息,似乎需要從 Source 目錄中的.nupkg文件中提取.nuspec文件,讀取相應框架的<group>並加載這些包的程序集。

以下方法可解決此問題,但請注意,它首先需要使用dotnet CLI 下載和安裝.NET SDK

  • 為要添加包的輔助項目創建一個文件夾並更改為該文件夾; 例如:

    • Set-Location (New-Item -Type Directory assemblies)
  • 在該文件夾中,創建一個虛擬庫項目:

    • dotnet new classlib
  • 添加對感興趣的包的引用; 例如:

    • dotnet add package Dapper

    • 要引用特定版本,請添加-v <version>

  • 發布虛擬項目,它將所有需要的 DLL(包括依賴項)復制到發布文件夾中:

    • dotnet publish -c Release
    • 重要提示-c參數的確切大小寫(小寫與大寫)決定了相應輸出文件夾的確切大小寫; 為確保您的代碼也適用於區分大小寫的文件系統,尤其是在 Linux 上,請確保在引用輸出二進制文件的文件路徑中使用完全相同的大小寫
  • 測試包的主程序集是否可以加載; 例如:

    • Add-Type -Path bin/Release/*/publish/Dapper.dll

    • 驗證包的類型是否可以使用; 例如: [Dapper.DbString]::new()

現在,您可以直接從輔助項目中引用主 DLL,也可以將所有bin/Release/*/publish/*.dll文件復制到您選擇的文件夾並從那里引用。

以下示例腳本顯示了一個腳本,該腳本根據需要下載Terminal.Gui包,並在相對於腳本位置的assemblies集子文件夾中創建輔助項目。

$packageName = 'Terminal.Gui'
$assembly = "$packageName.dll"
# Set to @() to get the latest stable version.
$packageVersionArgs = '-v', '1.0.0-pre.4'

$projectFolder = 'assemblies' # Subfolder for the aux. project
$assemblyPath = "$PSScriptRoot/$projectFolder/bin/Release/*/publish/$assembly"
$literalAssemblyPath = Convert-Path -ErrorAction Ignore $assemblyPath

if ($literalAssemblyPath) {
  Write-Verbose -vb "Package '$packageName' already installed. Loading main assembly: $literalAssemblyPath"
  Add-Type -ErrorAction Stop -LiteralPath $literalAssemblyPath
}
else {

  Write-Verbose -vb "Installing package '$packageName'..."

  $null = Get-Command -ErrorAction Stop -CommandType Application dotnet

  Push-Location (New-Item -ErrorAction Stop -Type Directory "$PSScriptRoot/$projectFolder")

  $null = dotnet new classlib
  $null = dotnet add package $packageName @packageVersionArgs
  $null = dotnet publish -c Release
  
  Pop-Location

  Write-Verbose -vb "Loading main assembly: $assemblyPath"  
  Add-Type -ErrorAction Stop -Path $assemblyPath
}

# Instantiate a type from the package to verify that it was loaded.
"Listing property names of a [Terminal.Gui.Button] instance:"
[Terminal.Gui.Button]::new().psobject.Properties.Name

警告

  • 某些包依賴於本機庫dotnet publish這些放在發布文件夾的runtimes子文件夾樹中,在特定於平台的子文件夾中,例如runtimes\\win-x64\\native

  • Windows PowerShell中, Add-Type -LiteralPath (及其底層的.NET API方法, [System.Reflection.Assembly]::LoadFrom()找到適合平台的本地庫,但是,奇怪的是,這行不通的作為PowerShell (Core) 7.2.0-preview.9 - 至少在Microsoft.Data.Sqlite NuGet 包的 5.0.9 版中觀察到。

  • 解決方法是在runtimes子文件夾樹中找到適合平台的本機庫,並將其直接復制到發布文件夾中。 本答案中討論的按需安裝Add-NuGetType幫助程序功能可自動執行此過程。


原答案

  • 如前所述,PowerShell v5+ - 包括 PowerShell Core - 帶有PackageManagement模塊,它是一個元包管理器,通過提供者提供對多個存儲庫的訪問; 在 v3 和 v4 中可能可以按需安裝此模塊( 此下載標記為“2016 年 3 月預覽版”,這是我能找到的最新版本)。

    • Find-PackageProvider列出所有可用的提供程序。
    • Get-PackageProvider列出已安裝的。
  • nuget提供程序允許通過Install-Package安裝 Nuget 包,但有兩個潛在的障礙

    • 可能未安裝nuget提供程序。

    • 它可能安裝了不正確的 API URL,從而阻止Find-Package返回結果。

測試是否安裝了nuget提供程序

# If this fails, the provider isn't installed
Get-PackageProvider nuget

如果安裝:驗證包源 URI 是否正確:

  • 打開提升的PowerShell 會話。
  • 運行Get-PackageSource
    • 如果您找到Nugettest源,請將其刪除:
      • Unregister-PackageSource Nugettest
    • 如果源nuget.orgLocation列顯示https://api.nuget.org/v3/index.json (或ttps://www.nuget.org/api/v2以外的其他ttps://www.nuget.org/api/v2 ),請更新它:
    • Set-PackageSource nuget.org -NewLocation https://www.nuget.org/api/v2 -Trusted
    • 警告:這可能會破壞在 Visual Studio 中瀏覽 NuGet 包的能力:請參閱https://github.com/PowerShell/PowerShellGet/issues/107

如果安裝:從頭開始安裝提供程序

  • 打開提升的PowerShell 會話。

  • 運行以下命令:

     Install-PackageProvider nuget Register-PackageSource -ProviderName nuget -name nuget.org -Location https://www.nuget.org/api/v2 -Trusted

完成上述步驟后,NuGet 包的發現(例如Find-Package Dapper )和安裝(例如Install-Package Dapper )應該會成功。

默認情況下, Install-Package安裝在AllUsers范圍內,這需要提升,但您可以選擇僅使用-Scope CurrentUser在當前用戶的上下文中安裝。


使用下載的 NuGet 包

注意:請參閱GitHub 上的此建議,通過擴展Add-Type在 PowerShell 中更輕松地使用 NuGet 包,這將消除所有后續步驟的需要,從 PowerShell Core 6.2.0 開始仍然需要這些步驟。

  • 如問題所示,您需要使用Add-Type -Path <assembly-file-path>手動將包的程序集加載到 PowerShell 會話中 但是,在.NET Core時代,包可能有針對不同.NET環境的DLL,所以你不能總是盲目地加載包文件夾中的所有*.dll文件

  • 為了發現下載包的文件系統位置,查詢Get-Package返回的相關對象的.Source屬性:

     (Get-Package Dapper).Source
  • 查看包內所有 DLL 的完整路徑,請運行以下命令:

     (Get-ChildItem -Filter *.dll -Recurse (Split-Path (Get-Package Dapper).Source)).FullName
  • 查看完整的 DLL 路徑應該會告訴您哪些 DLL 適合為您的環境加載; 使用Dapper包的示例:

     C:\\Program Files\\PackageManagement\\NuGet\\Packages\\Dapper.1.50.4\\lib\\net451\\Dapper.dll C:\\Program Files\\PackageManagement\\NuGet\\Packages\\Dapper.1.50.4\\lib\\netstandard1.3\\Dapper.dll C:\\Program Files\\PackageManagement\\NuGet\\Packages\\Dapper.1.50.4\\lib\\netstandard2.0\\Dapper.dll
  • 但是,鑒於.NET Standard DLL 可在所有.NET 平台上運行,您可以通過編程方式查找(最新的)此類 DLL 並加載它們:

     (Get-Item (Join-Path (Split-Path (Get-Package Dapper).Source) lib/netstandard*) | Sort-Object { [version] ($_.Name -replace '^netstandard') })[-1] | Get-ChildItem -Filter *.dll -Recurse | ForEach-Object { Add-Type -LiteralPath $_.FullName }
    • 以上尋找最高可用的.NET Standard 版本 DLL; 如果你想針對特定版本,命令會變得更容易; 例如,對於 .NET Standard 2.0

       Get-ChildItem -Recurse -Filter *.dll -LiteralPath (Join-Path (Split-Path (Get-Package Dapper).Source) lib/netstandard2.0) | ForEach-Object { Add-Type -LiteralPath $_.FullName }

您在使用 Powershell 5 嗎? 因為如果你是,它有一個包管理模塊:

Powershell 5 中的 PackageManagement 模塊

它似乎是開源的: https : //github.com/OneGet

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM