简体   繁体   English

从 NuGet 包加载程序集

[英]Loading Assemblies from NuGet Packages

Sometimes in my PowerShell scripts, I need access to a specific DLL, using Add-Type -AssemblyName .有时在我的 PowerShell 脚本中,我需要使用Add-Type -AssemblyName访问特定的 DLL。 However, the DLLs I need aren't always on the machine or in the GAC.但是,我需要的 DLL 并不总是在机器上或 GAC 中。 For instance, I might want a quick script that uses Dapper to query a database.例如,我可能想要一个使用 Dapper 查询数据库的快速脚本。 In these cases, I have been literally copying the DLLs along with the ps1 file.在这些情况下,我一直在复制 DLL 和ps1文件。 I was wondering if this was common/a good idea and whether there was an existing extension that would load up NuGet packages, store then in a global or local folder and call Add-Type -AssemblyName automatically.我想知道这是否常见/一个好主意,以及是否有一个现有的扩展可以加载 NuGet 包,然后将其存储在全局或本地文件夹中并自动调用Add-Type -AssemblyName

It'd be a lot like using npm or pip in Node.js or Python, respectively.这很像分别在 Node.js 或 Python 中使用npmpip

Update更新

I did some research and there's nothing built-in to older versions of PowerShell.我做了一些研究,发现旧版本的 PowerShell 没有内置任何内容。 I made some progress trying to write one from scratch using the nuget.exe我在尝试使用nuget.exe从头开始编写一个时取得了一些进展

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

This will download a given package/version under a "packages" folder in the current folder, along with any of its dependencies.这将下载当前文件夹中“packages”文件夹下的给定包/版本,以及它的任何依赖项。 However, it looks like it downloads every framework version, with no obvious way to tell which one to use for your given environment.但是,看起来它会下载每个框架版本,但没有明显的方法来告诉您在给定环境中使用哪个版本。

Otherwise, you could just loop through the results and call Add-Type:否则,您可以循环遍历结果并调用 Add-Type:

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

I tried using the restore command using a project.json file to see if I could control the framework version with no luck.我尝试通过project.json文件使用restore命令来查看是否可以在没有运气的情况下控制框架版本。 This is just too hacky for me.这对我来说太hacky了。

I'll check out @crownedjitter's suggestion of using PowerShell 5.我将查看@crownjitter 关于使用 PowerShell 5 的建议。

Update更新

Using @crownedjitter's suggestion, I was able to eventually register the PackageManagement module with NuGet (see comments below).使用@crownjitter 的建议,我最终能够使用 NuGet 注册 PackageManagement 模块(请参阅下面的评论)。 With the following command, I was able to reproduce what the Nuget.exe command above was doing:使用以下命令,我能够重现上面的Nuget.exe命令正在执行的操作:

Install-Package Dapper -Destination packages

Obviously, this is a lot shorter.显然,这要短得多。 Problem is it has the same limitation;问题是它有同样的限制; it brings down every framework version of a package.它降低了包的每个框架版本。 If this includes .NET core, it brings down a good deal of the .NET core framework with it!如果这包括 .NET 核心,它会降低很多 .NET 核心框架! There doesn't appear to be a way to specify a target framework (aka, .NET 4.5.1 or below).似乎没有办法指定目标框架(也就是 .NET 4.5.1 或更低版本)。

安装包抓取所有 .NET 框架的依赖项

I am wondering if there is a way to determine which NuGet package folder(s) to load the DLLs from based on PowerShell's current $PSVersionTable.CLRVersion field.我想知道是否有办法根据 PowerShell 当前的$PSVersionTable.CLRVersion字段确定从哪个 NuGet 包文件夹加载 DLL。

crownedjitter's helpful answer is a good starting point, and Travis himself has provided additional pointers in comments, but let me try to summarize as of Windows PowerShell v5.1 / PowerShell [Core] 7.1 : Crownedjitter 的有用答案是一个很好的起点,Travis 本人在评论中提供了额外的指示,但让我尝试从 Windows PowerShell v5.1 / PowerShell [Core] 7.1开始总结

Update : The original answer below contains some useful general pointers, plus a link to the feature suggestion on GitHub to integrate NuGet packages with Add-Type , but the Install-Package based method it shows ultimately falls short , because it doesn't account for dependencies of a package , as BACON points out:更新:下面的原始答案包含一些有用的一般指针,以及指向GitHub 上功能建议的链接,以将 NuGet 包与Add-Type集成,但它显示的基于Install-Package的方法最终达不到要求,因为它没有考虑包的依赖关系,正如BACON指出的那样:

The one (not-so-trivial) step that's missing is loading any dependencies that may have been installed, too.缺少的一个(非常重要的)步骤是加载任何可能已安装的依赖项。 Because the Dependencies property doesn't contain enough information , it seems that would involve extracting the .nuspec file from the .nupkg file in the Source directory, reading the <group> for the appropriate framework, and loading those packages' assemblies.由于Dependencies 属性不包含足够的信息,似乎需要从 Source 目录中的.nupkg文件中提取.nuspec文件,读取相应框架的<group>并加载这些包的程序集。

The following approach remedies this , but note that it first requires download and installation of the .NET SDK with its dotnet CLI :以下方法可解决此问题,但请注意,它首先需要使用dotnet CLI 下载和安装.NET SDK

  • Create a folder for an auxiliary project to which the package will be added and change to it;为要添加包的辅助项目创建一个文件夹并更改为该文件夹; eg:例如:

    • Set-Location (New-Item -Type Directory assemblies)
  • In that folder, create a dummy library project:在该文件夹中,创建一个虚拟库项目:

    • dotnet new classlib
  • Add a reference to the package of interest;添加对感兴趣的包的引用; eg:例如:

    • dotnet add package Dapper

    • To reference a specific version, add -v <version>要引用特定版本,请添加-v <version>

  • Publish the dummy project, which copies all required DLLs, including dependencies, into the publish folder:发布虚拟项目,它将所有需要的 DLL(包括依赖项)复制到发布文件夹中:

    • dotnet publish -c Release
    • Important : The exact case (lower- vs. uppercase) of the -c argument determines the exact case of the corresponding output folder;重要提示-c参数的确切大小写(小写与大写)决定了相应输出文件夹的确切大小写; to make sure your code works on case-sensitive filesystems too, notably on Linux, make sure that you use the exact same case in the file paths referring to the output binaries .为确保您的代码也适用于区分大小写的文件系统,尤其是在 Linux 上,请确保在引用输出二进制文件的文件路径中使用完全相同的大小写
  • Test that the package's main assembly can be loaded;测试包的主程序集是否可以加载; eg:例如:

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

    • Verify that the package's types can be used;验证包的类型是否可以使用; eg: [Dapper.DbString]::new()例如: [Dapper.DbString]::new()

Now you can either reference the main DLL directly from the auxiliary project, or you can copy all bin/Release/*/publish/*.dll files to a folder of your choice and reference it from there.现在,您可以直接从辅助项目中引用主 DLL,也可以将所有bin/Release/*/publish/*.dll文件复制到您选择的文件夹并从那里引用。

The following sample script shows a script that downloads the Terminal.Gui package on demand, and creates the auxiliary project in an assemblies subfolder relative to the script's location.以下示例脚本显示了一个脚本,该脚本根据需要下载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

Caveat :警告

  • Some packages have dependencies on native libraries , which dotnet publish places in the runtimes subfolder tree of the publish folder, in platform-specific subfolders such as runtimes\\win-x64\\native .某些包依赖于本机库dotnet publish这些放在发布文件夹的runtimes子文件夹树中,在特定于平台的子文件夹中,例如runtimes\\win-x64\\native

  • In Windows PowerShell , Add-Type -LiteralPath (and its underlying .NET API method, [System.Reflection.Assembly]::LoadFrom() ) does find the platform-appropriate native library, but, curiously, it does not work as of PowerShell (Core) 7.2.0-preview.9 - at least as observed with version 5.0.9 of the Microsoft.Data.Sqlite NuGet package.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 版中观察到。

  • The workaround is to find the platform-appropriate native library in the runtimes subfolder tree and copy it directly into the publish folder.解决方法是在runtimes子文件夹树中找到适合平台的本机库,并将其直接复制到发布文件夹中。 The install-on-demand Add-NuGetType helper function, discussed in this answer , automates this process.本答案中讨论的按需安装Add-NuGetType帮助程序功能可自动执行此过程。


ORIGINAL ANSWER原答案

  • As stated, PowerShell v5+ - including PowerShell Core - comes with the PackageManagement module that is a meta package manager providing access to multiple repositories via providers ;如前所述,PowerShell v5+ - 包括 PowerShell Core - 带有PackageManagement模块,它是一个元包管理器,通过提供者提供对多个存储库的访问; on-demand installation of this module is may be possible in v3 and v4 ( this download is labeled "March 2016 Preview", and it is the most recent I could find).在 v3 和 v4 中可能可以按需安装此模块( 此下载标记为“2016 年 3 月预览版”,这是我能找到的最新版本)。

    • Find-PackageProvider lists all available providers. Find-PackageProvider列出所有可用的提供程序。
    • Get-PackageProvider lists installed ones. Get-PackageProvider列出已安装的。
  • It is the nuget provider that enables installation of Nuget packages via Install-Package , and there are two potential hurdles : nuget提供程序允许通过Install-Package安装 Nuget 包,但有两个潜在的障碍

    • The nuget provider may not be installed.可能未安装nuget提供程序。

    • It may be installed with an incorrect API URL that prevents Find-Package from returning results.它可能安装了不正确的 API URL,从而阻止Find-Package返回结果。

Test if the nuget provider is installed :测试是否安装了nuget提供程序

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

If it is installed: Verify that the package source URI is correct:如果安装:验证包源 URI 是否正确:

  • Open an elevated PowerShell session.打开提升的PowerShell 会话。
  • Run Get-PackageSource :运行Get-PackageSource
    • If you find a Nugettest source, remove it:如果您找到Nugettest源,请将其删除:
      • Unregister-PackageSource Nugettest
    • If the Location column for source nuget.org shows https://api.nuget.org/v3/index.json (or something other than ttps://www.nuget.org/api/v2 ), update it:如果源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
    • Caveat : This may break the ability to browse NuGet packages in Visual Studio: see https://github.com/PowerShell/PowerShellGet/issues/107警告:这可能会破坏在 Visual Studio 中浏览 NuGet 包的能力:请参阅https://github.com/PowerShell/PowerShellGet/issues/107

If it is not installed: Install the provider from scratch :如果安装:从头开始安装提供程序

  • Open an elevated PowerShell session.打开提升的PowerShell 会话。

  • Run the following commands:运行以下命令:

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

After completing the above steps, discovery (eg, Find-Package Dapper ) and installation (eg, Install-Package Dapper ) of NuGet packages should succeed.完成上述步骤后,NuGet 包的发现(例如Find-Package Dapper )和安装(例如Install-Package Dapper )应该会成功。

By default, Install-Package installs in the AllUsers scope, which requires elevation, but you can opt into installing in the context of the current user only with -Scope CurrentUser .默认情况下, Install-Package安装在AllUsers范围内,这需要提升,但您可以选择仅使用-Scope CurrentUser在当前用户的上下文中安装。


Using a downloaded NuGet package :使用下载的 NuGet 包

Note: See this suggestion on GitHub for making the use of NuGet packages in PowerShell easier by extending Add-Type , which would obviate the need for all subsequent steps, which are still needed as of PowerShell Core 6.2.0.注意:请参阅GitHub 上的此建议,通过扩展Add-Type在 PowerShell 中更轻松地使用 NuGet 包,这将消除所有后续步骤的需要,从 PowerShell Core 6.2.0 开始仍然需要这些步骤。

  • As demonstrated in the question, you need to manually load the package's assemblies into your PowerShell session with Add-Type -Path <assembly-file-path> ;如问题所示,您需要使用Add-Type -Path <assembly-file-path>手动将包的程序集加载到 PowerShell 会话中 however, in the era of .NET Core, packages may have DLLs for different .NET environments, so you cannot always blindly load all *.dll files in the package folder :但是,在.NET Core时代,包可能有针对不同.NET环境的DLL,所以你不能总是盲目地加载包文件夹中的所有*.dll文件

  • In order to discover the file-system location of a downloaded package, query the .Source property of the relevant object returned by Get-Package :为了发现下载包的文件系统位置,查询Get-Package返回的相关对象的.Source属性:

     (Get-Package Dapper).Source
  • To see the full paths of all DLLs inside the package , run the following:查看包内所有 DLL 的完整路径,请运行以下命令:

     (Get-ChildItem -Filter *.dll -Recurse (Split-Path (Get-Package Dapper).Source)).FullName
  • Looking at the full DLL paths should tell you which DLL(s) are the right ones to load for your environment;查看完整的 DLL 路径应该会告诉您哪些 DLL 适合为您的环境加载; using the example of the Dapper package:使用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
  • Given that .NET Standard DLLs run on all .NET platforms , however, you can programmatically look for the (latest) such DLLs and load them:但是,鉴于.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 }
    • The above looks for the highest available .NET Standard version DLLs;以上寻找最高可用的.NET Standard 版本 DLL; if you want to target a specific version , the command becomes easier;如果你想针对特定版本,命令会变得更容易; eg, for .NET Standard 2.0 :例如,对于 .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 }

Are you using Powershell 5?您在使用 Powershell 5 吗? Because if you are, it has a package management module:因为如果你是,它有一个包管理模块:

Powershell 5 中的 PackageManagement 模块

It appears to be open source: https://github.com/OneGet它似乎是开源的: https : //github.com/OneGet

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM