问题描述
有时在我的 PowerShell 脚本中,我需要使用 Add-Type -AssemblyName
访问特定的 DLL.但是,我需要的 DLL 并不总是在机器上或 GAC 中.例如,我可能想要一个使用 Dapper 查询数据库的快速脚本.在这些情况下,我一直在复制 DLL 和 ps1
文件.我想知道这是否常见/一个好主意,以及是否有现有的扩展可以加载 NuGet 包,然后将其存储在全局或本地文件夹中并自动调用 Add-Type -AssemblyName
.
Sometimes in my PowerShell scripts, I need access to a specific DLL, using Add-Type -AssemblyName
. However, the DLLs I need aren't always on the machine or in the GAC. For instance, I might want a quick script that uses Dapper to query a database. In these cases, I have been literally copying the DLLs along with the ps1
file. 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.
这很像分别在 Node.js 或 Python 中使用 npm
或 pip
.
It'd be a lot like using npm
or pip
in Node.js or Python, respectively.
我做了一些研究,发现旧版本的 PowerShell 没有内置任何内容.我在尝试使用 nuget.exe
I did some research and there's nothing built-in to older versions of PowerShell. I made some progress trying to write one from scratch using the nuget.exe
&"$(Get-Location)/nuget.exe" install $packageName -Version $version -OutputDirectory "$(Get-Location)/packages" -NoCache -NoInteractive
这将在packages"目录下下载给定的包/版本.当前文件夹中的文件夹,以及它的任何依赖项.但是,它似乎会下载每个框架版本,但没有明显的方法来告诉您在给定环境中使用哪个版本.
This will download a given package/version under a "packages" folder in the current folder, along with any of its dependencies. However, it looks like it downloads every framework version, with no obvious way to tell which one to use for your given environment.
否则,您可以循环遍历结果并调用 Add-Type:
Otherwise, you could just loop through the results and call Add-Type:
Get-ChildItem .\packages\ -Recurse -Filter "*.dll" | % {
try
{
Add-Type -Path $_.FullName
}
catch [System.Exception]
{
}
}
我尝试使用 project.json
文件使用 restore
命令,看看我是否可以控制框架版本,但没有运气.这对我来说太hacky了.
I tried using the restore
command using a project.json
file to see if I could control the framework version with no luck. This is just too hacky for me.
我会查看@crownjitter 关于使用 PowerShell 5 的建议.
I'll check out @crownedjitter's suggestion of using PowerShell 5.
使用@crownjitter 的建议,我最终能够使用 NuGet 注册 PackageManagement 模块(请参阅下面的评论).使用以下命令,我能够重现上面的 Nuget.exe
命令正在执行的操作:
Using @crownedjitter's suggestion, I was able to eventually register the PackageManagement module with NuGet (see comments below). With the following command, I was able to reproduce what the Nuget.exe
command above was doing:
Install-Package Dapper -Destination packages
显然,这要短得多.问题是它有同样的限制;它降低了包的每个框架版本.如果这包括 .NET 核心,它会降低很多 .NET 核心框架!似乎没有办法指定目标框架(也就是 .NET 4.5.1 或更低版本).
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! There doesn't appear to be a way to specify a target framework (a.k.a, .NET 4.5.1 or below).
我想知道是否有办法根据 PowerShell 当前的 $PSVersionTable.CLRVersion
字段确定从哪个 NuGet 包文件夹加载 DLL.
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.
推荐答案
crownedjitter 的有用回答 是一个很好的起点,Travis 本人在评论中提供了额外的指示,但让我尝试总结 Windows PowerShell v5.1/PowerShell [Core] 7.1:
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:
更新:下面的原始答案包含一些有用的通用指针,以及一个指向 GitHub 上的功能建议将 NuGet 包与 Add-Type
集成,但 Install-Package
它显示的基于方法的最终不足,因为它没有考虑包的依赖项,因为BACON 指出:
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, asBACON points out:
缺少的一个(非常重要的)步骤是加载任何可能已安装的依赖项.因为 Dependencies 属性没有包含足够的信息,似乎需要提取.nuspec
文件来自 Source 目录中的 .nupkg
文件,读取相应框架的 并加载这些包程序集.
以下方法可解决此问题,但请注意,它首先需要下载并安装 .NET SDK 及其dotnet
CLI:
The following approach remedies this, but note that it first requires download and installation of the .NET SDK with its dotnet
CLI:
为要添加包的辅助项目创建一个文件夹并更改为该文件夹;例如:
Create a folder for an auxiliary project to which the package will be added and change to it; e.g.:
Set-Location(新项目-类型目录程序集)
在该文件夹中,创建一个虚拟库项目:
In that folder, create a dummy library project:
dotnet new classlib
添加对感兴趣的包的引用;例如:
Add a reference to the package of interest; e.g.:
dotnet 添加包 Dapper
要引用特定版本,请添加 -v
To reference a specific version, add -v <version>
发布虚拟项目,该项目将所有必需的 DLL(包括依赖项)复制到发布文件夹中:
Publish the dummy project, which copies all required DLLs, including dependencies, into the publish folder:
dotnet publish -c Release
- 重要:
-c
参数的确切大小写(小写与大写)决定了相应输出文件夹的确切大小写;为确保您的代码也适用于区分大小写的文件系统,尤其是在 Linux 上,确保在引用输出二进制文件的文件路径中使用完全相同的大小写.
dotnet publish -c Release
- Important: The exact case (lower- vs. uppercase) of the
-c
argument determines the exact case of the corresponding output folder; 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; e.g.:
Add-Type -Path bin/Release/*/publish/Dapper.dll
验证包的类型是否可以使用;例如:[Dapper.DbString]::new()
Verify that the package's types can be used; e.g.: [Dapper.DbString]::new()
现在您可以直接从辅助项目引用主 DLL,也可以将所有 bin/Release/*/publish/*.dll
文件复制到您选择的文件夹并引用它从那里开始.
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.
以下示例脚本显示了下载终端的脚本.Gui 按需打包,并在相对于脚本位置的 assemblies
子文件夹中创建辅助项目.
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.
$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
原答案
如前所述,PowerShell v5+ - 包括 PowerShell Core - 带有
PackageManagement
模块,该模块是一个元包管理器,通过 提供对多个存储库的访问提供者;在 v3 和 v4 (此下载 标记为2016 年 3 月预览版",这是我能找到的最新版本.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; 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).Find-PackageProvider
列出所有可用提供者.Get-PackageProvider
列出已安装的.
Find-PackageProvider
lists all available providers.Get-PackageProvider
lists installed ones.
它是
nuget
提供程序,它允许通过Install-Package
安装 Nuget 包,并且有 两个潜在障碍:It is the
nuget
provider that enables installation of Nuget packages viaInstall-Package
, and there are two potential hurdles:可能未安装
nuget
提供程序.
它可能安装了不正确的 API URL,导致
Find-Package
无法返回结果.It may be installed with an incorrect API URL that prevents
Find-Package
from returning results.测试是否安装了
nuget
提供程序:Test if the
nuget
provider is installed:# If this fails, the provider isn't installed Get-PackageProvider nuget
如果安装了:验证包源 URI 是否正确:
If it is installed: Verify that the package source URI is correct:
- 打开一个 提升 PowerShell 会话.
- 运行
Get-PackageSource
:- 如果您找到
Nugettest
源,请将其删除:Unregister-PackageSource Nugettest
- Open an elevated PowerShell session.
- Run
Get-PackageSource
:- If you find a
Nugettest
source, remove it:Unregister-PackageSource Nugettest
如果未安装:从头开始安装提供程序:
If it is not installed: Install the provider from scratch:
打开一个 提升 PowerShell 会话.
Open an elevated PowerShell session.
运行以下命令:
Install-PackageProvider nuget Register-PackageSource -ProviderName nuget -name nuget.org -Location https://www.nuget.org/api/v2 -Trusted
如问题所示,您需要使用
Add-Type -Path <assembly-file-path>
将包的程序集手动加载到 PowerShell 会话中强>;但是,在.NET Core 时代,包中可能有针对不同.NET 环境的DLL,所以你不能总是盲目地加载所有*.dll
文件中的*.dll
包文件夹: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>
; 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:
完成上述步骤后,NuGet 包的发现(例如,
Find-Package Dapper
)和安装(例如,Install-Package Dapper
)应该会成功.After completing the above steps, discovery (e.g.,
Find-Package Dapper
) and installation (e.g.,Install-Package Dapper
) of NuGet packages should succeed.默认情况下,
Install-Package
安装在AllUsers
范围内,这需要提升,但您可以选择仅使用-Scope CurrentUser
.By default,
Install-Package
installs in theAllUsers
scope, which requires elevation, but you can opt into installing in the context of the current user only with-Scope CurrentUser
.使用下载的 NuGet 包:
Using a downloaded NuGet package:
注意:请参阅 GitHub 上的此建议,了解如何使用 NuGet 包通过扩展
Add-Type
使 PowerShell 更容易,这将消除所有后续步骤的需要,从 PowerShell Core 6.2.0 开始仍然需要这些步骤.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.为了发现下载包的文件系统位置,查询
Get-Package
返回的相关对象的.Source
属性:In order to discover the file-system location of a downloaded package, query the
.Source
property of the relevant object returned byGet-Package
:(Get-Package Dapper).Source
- If you find a
要查看包内所有 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 并加载它们:
Given that .NET Standard DLLs run on all .NET platforms, however, you can programmatically look for the (latest) such DLLs and load them:
(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
:The above looks for the highest available .NET Standard version DLLs; if you want to target a specific version, the command becomes easier; e.g., for .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 }
这篇关于从 NuGet 包加载程序集的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
- 如果您找到