Загрузка сборок из пакетов NuGet

Иногда в сценариях PowerShell мне нужен доступ к определенной DLL, используя Add-Type -AssemblyName. Однако DLL, которые мне нужны, не всегда находятся на машине или в GAC. Например, мне может понадобиться быстрый скрипт, который использует Dapper для запроса базы данных. В этих случаях я буквально копировал библиотеки DLL вместе с файлом ps1. Мне было интересно, было ли это общепринято/хорошая идея и было ли существующее расширение, которое будет загружать пакеты NuGet, хранить их в глобальной или локальной папке и автоматически вызывать Add-Type -AssemblyName.

Это было бы очень похоже на использование npm или pip в Node.js или Python, соответственно.

Обновить

Я провел несколько исследований и ничего не добавил в более старые версии PowerShell. Я сделал некоторый прогресс, пытаясь написать его с нуля, используя nuget.exe

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

Это загрузит данный пакет/версию в папку "пакеты" в текущей папке вместе с любой из ее зависимостей. Тем не менее, похоже, что он загружает каждую версию фреймворка без очевидного способа узнать, какой из них использовать для данной среды.

В противном случае вы можете просто просмотреть результаты и вызвать Add-Type:

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

Я попытался использовать команду restore используя файл project.json чтобы проверить, могу ли я управлять версией фреймворка без везения. Это слишком тяжело для меня.

Я рассмотрю предложение @crownedjitter об использовании PowerShell 5.

Обновить

Используя предложение @crownedjitter, я смог в конечном итоге зарегистрировать модуль PackageManagement с NuGet (см. Комментарии ниже). Следуя команде, я смог воспроизвести то, что Nuget.exe команда Nuget.exe выше:

Install-Package Dapper -Destination packages

Очевидно, это намного короче. Проблема в том, что она имеет такое же ограничение; он сбрасывает каждую версию пакета. Если это включает в себя ядро .NET, оно сбивает с собой значительную часть основной среды.NET! Кажется, что не существует способа указать целевую структуру (например,.NET 4.5.1 или ниже).

Install-Package grabbing dependencies for all .NET frameworks

Мне интересно, есть ли способ определить, какие пакеты пакетов NuGet загружать библиотеки DLL на основе PowerShell текущего поля $PSVersionTable.CLRVersion.

Ответ 1

Полезный ответ crownedjitter является хорошей отправной точкой, и сам Тревис предоставил дополнительные ссылки в комментариях, но позвольте мне подвести итог, начиная с Windows PowerShell v5.1:

  • Как уже говорилось, PowerShell v5+, включая PowerShell Core, поставляется с модулем PackageManagement который представляет собой менеджер метапакетов, обеспечивающий доступ к нескольким хранилищам через провайдеров; установка этого модуля по требованию возможна в версиях 3 и 4 (эта загрузка помечена как "Предварительный просмотр за март 2016 года", и это самая последняя версия, которую я смог найти).

    • Find-PackageProvider перечисляет всех доступных провайдеров.
    • Get-PackageProvider перечисляет установленные.
  • Это nuget поставщик, который позволяет устанавливать пакеты NuGet через Install-Package, и есть два возможных препятствия:

    • Возможно, поставщик nuget не установлен.

    • Он может быть установлен с неверным URL-адресом API, который не позволяет Find-Package возвращать результаты.

Проверьте, установлен ли поставщик nuget:

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

Если он установлен: убедитесь, что исходный URI пакета верен:

  • Откройте сеанс PowerShell с повышенными правами.
  • Запустите Get-PackageSource:
    • Если вы найдете источник Nugettest, удалите его:
      • Unregister-PackageSource Nugettest
    • Если в столбце " Location для источника nuget.org отображается https://api.nuget.org/v3/index.json (или что-то другое, кроме ttps://www.nuget.org/api/v2), обновите его:
      • Set-PackageSource nuget.org -NewLocation https://www.nuget.org/api/v2 -Trusted
      • Предостережение. Это может нарушить возможность просмотра пакетов NuGet в Visual Studio: см. 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
    

После выполнения описанных выше шагов, обнаружение (например, Find-Package Dapper) и установка (например, Install-Package Dapper) пакетов NuGet должны завершиться успешно.

По умолчанию Install-Package устанавливается в области AllUsers, что требует повышения AllUsers, но вы можете отказаться от установки в контексте текущего пользователя только с -Scope CurrentUser.


Используя загруженный пакет NuGet:

Примечание. Посмотрите это предложение на GitHub, чтобы упростить использование пакетов NuGet в PowerShell, расширив Add-Type, что устранит необходимость во всех последующих шагах, которые по-прежнему необходимы в PowerShell Core 6.2.0.

  • Как показано в этом вопросе, вам необходимо вручную загрузить сборки пакетов в сеанс PowerShell с помощью Add-Type -Path <assembly-file-Path>; однако в эпоху .NET Core пакеты могут иметь библиотеки DLL для разных сред .NET, поэтому вы не всегда можете вслепую загружать все файлы *.dll в папку пакета:

  • Чтобы .Source местоположение файловой системы загруженного пакета, запросите свойство .Source соответствующего объекта, возвращаемого Get-Package:

    (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 работают на всех платформах .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 }
    
    • Вышесказанное ищет библиотеки DLL стандартной версии .NET; если вы хотите указать конкретную версию, команда станет проще; например, для .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 }
      

Бэкон отмечает, что вышеприведенного недостаточно, если у пакета есть зависимости, которые также должны быть загружены:

Единственный (не очень тривиальный) шаг, который отсутствует, - это загрузка любых зависимостей, которые также могли быть установлены. Поскольку свойство Dependencies не содержит достаточной информации, кажется, что это потребует извлечения файла .nuspec из файла .nupkg в каталоге Source, чтения <group> для соответствующей платформы и загрузки сборок этих пакетов.

Ответ 2

Вы используете Powershell 5? Потому что если у вас есть модуль управления пакетами:

PackageManagement Module in Powershell 5

Похоже, с открытым исходным кодом: https://github.com/OneGet