[英]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 中使用
npm
或pip
。
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 的建议。
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 或更低版本)。
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
-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 . 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
帮助程序功能可自动执行此过程。
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 是否正确:
Get-PackageSource
:Get-PackageSource
:
Nugettest
source, remove it:Nugettest
源,请将其删除:
Unregister-PackageSource Nugettest
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.org
的Location
列显示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
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:
因为如果你是,它有一个包管理模块:
It appears to be open source: https://github.com/OneGet它似乎是开源的: https : //github.com/OneGet
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.