繁体   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