Як завантажити збірки в PowerShell?


153

Наступний код PowerShell

#Get a server object which corresponds to the default instance
$srv = New-Object -TypeName Microsoft.SqlServer.Management.SMO.Server
... rest of the script ...

Подає таке повідомлення про помилку:

New-Object : Cannot find type [Microsoft.SqlServer.Management.SMO.Server]: make sure 
the assembly containing this type is loaded.
At C:\Users\sortelyn\ ... \tools\sql_express_backup\backup.ps1:6  char:8
+ $srv = New-Object -TypeName Microsoft.SqlServer.Management.SMO.Server
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

Кожна відповідь в Інтернеті пише, що я повинен завантажити збірку - я впевнений, що я можу прочитати це з повідомлення про помилку :-) - питання:

Як ви завантажуєте збірку і змушуєте сценарій працювати?

Відповіді:


179

LoadWithPartialNameзастаріло. Рекомендованим рішенням для PowerShell V3 є використання Add-Typeкомандлета, наприклад:

Add-Type -Path 'C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.Smo.dll'

Існує кілька різних версій, і ви можете вибрати конкретну версію. :-)


1
Гаразд я використовую PowerShell3 - сюди входять команди, здається, дуже складні. Я просто очікував, що щось на кшталт "включити ім'я файлу".
Бакстер

6
PowerShell нечутливий до регістру (якщо ви не скажете, що він відрізняється від регістру з такими операторами, як -cmatch, -ceq). Тому корпус для назв та параметрів команд не має значення.
Кіт Хілл

5
Так. msdn.microsoft.com/en-us/library/12xc5368(v=vs.110).aspx Дивіться примітку вгорі - This API is now obsolete. Звичайно, це не заважає людям користуватися нею.
Кіт Хілл

2
Хоча технічно правильно, що LoadWithPartialNameзастаріло, причини (як зазначено в blogs.msdn.com/b/suzcook/archive/2003/05/30/57159.aspx ) явно не застосовуються до інтерактивного сеансу Powershell. Я пропоную вам додати зауваження, що API чудово підходить для інтерактивного використання Powershell.
Micha Wiedenmann

У більшості випадків у мене немає проблем із складанням SMO, але іноді мені потрібно вбити паттерн, і коли я це роблю, у мене виникають проблеми із завантаженням SMO. Додавання типу add-type - виправляє це шлях.
Ніколя де Фонтеней

73
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")

8
Це занадто корисно, щоб застаріти без заміни! Моя команда використовує суміш клієнтських інструментів 2008 та 2012 років. Це єдиний спосіб змусити мої сценарії PowerShell працювати для всієї моєї команди, не включаючи незграбну логіку версії для резервної версії.
Ієн Самуель Маклін Старший

4
Ви можете передавати вихід, Out-Nullякщо не хочете, щоб GAC повторював речі.
Ієн Самуель Маклін Старший

3
@Baxter - ви повинні прийняти цю відповідь або Кіт, і нехай це питання буде позначено як відповідь.
Джейкуль

3
Я використовую [void] [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.Smo")
Soeren L. Nielsen,

@IainElder "незграбна версія запасної версії" Ви говорите, що поки не зіткнетеся з несумісністю версії! Це не так складно сказати Add-Type -Path [...]; if (!$?) { Add-Type -Path [...] } elseif [...].
Шматочки бекону

44

Більшість людей до цих пір знає, що System.Reflection.Assembly.LoadWithPartialNameце застаріле, але виявляється, що Add-Type -AssemblyName Microsoft.VisualBasic він веде себе не набагато краще, ніжLoadWithPartialName :

Замість того, щоб робити будь-які спроби аналізувати ваш запит у контексті вашої системи, [Add-Type] розглядає статичну внутрішню таблицю, щоб перевести "часткове ім'я" на "повне ім'я".

Якщо ваше "часткове ім'я" не відображається в їхній таблиці, ваш сценарій вийде з ладу.

Якщо на вашому комп'ютері встановлено кілька версій збірки, не існує інтелектуального алгоритму вибору між ними. Ви збираєтесь отримати те, що з’явиться в їх таблиці, ймовірно, старіше, застаріле.

Якщо встановлені вами версії новіші, ніж застарілі в таблиці, ваш скрипт вийде з ладу.

Add-Type не має інтелектуального аналізатора типу "часткових імен" .LoadWithPartialNames.

Microsoft каже, що ви насправді повинні зробити, це щось подібне:

Add-Type -AssemblyName 'Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

Або, якщо ви знаєте шлях, щось подібне:

Add-Type -Path 'C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\Microsoft.VisualBasic\v4.0_10.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualBasic.dll'

Ця довга назва, дана для зборів, відома як сильна назва , яка є унікальною як для версії, так і для складання, а також іноді називається повним ім'ям.

Але це залишає кілька питань без відповіді:

  1. Як визначити сильну назву того, що насправді завантажується в мою систему із заданим частковим іменем?

    [System.Reflection.Assembly]::LoadWithPartialName($TypeName).Location; [System.Reflection.Assembly]::LoadWithPartialName($TypeName).FullName;

Вони також повинні працювати:

Add-Type -AssemblyName $TypeName -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. Якщо я хочу, щоб мій сценарій завжди використовував певну версію .dll, але я не можу бути впевненим, де він встановлений, як я можу визначити, що таке сильне ім'я від .dll?

    [System.Reflection.AssemblyName]::GetAssemblyName($Path).FullName;

Або:

Add-Type $Path -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. Якщо я знаю сильне ім'я, як визначити шлях .dll?

    [Reflection.Assembly]::Load('Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a').Location;

  2. І, якщо я знаю назву типу того, що я використовую, як я знаю, з якої збірки вона йде?

    [Reflection.Assembly]::GetAssembly([Type]).Location [Reflection.Assembly]::GetAssembly([Type]).FullName

  3. Як я бачу, які збірки доступні?

Я пропоную модуль GAC PowerShell . Get-GacAssembly -Name 'Microsoft.SqlServer.Smo*' | Select Name, Version, FullNameпрацює досить добре.

  1. Як я бачу список, який Add-Typeвикористовує?

Це трохи складніше. Я можу описати, як отримати доступ до нього для будь-якої версії PowerShell з відбивачем .Net (див. Оновлення нижче для PowerShell Core 6.0).

Спочатку з’ясуйте, з якої бібліотеки Add-Typeпоходить:

Get-Command -Name Add-Type | Select-Object -Property DLL

Відкрийте отриману DLL за допомогою рефлектора. Я використовував для цього ILSpy, тому що це FLOSS, але будь-який C # рефлектор повинен працювати. Відкрийте цю бібліотеку і загляньте Microsoft.Powershell.Commands.Utility. Під Microsoft.Powershell.Commands, повинно бути AddTypeCommand.

У коді лістингу для цього є приватний клас InitializeStrongNameDictionary(). Тут перелічено словник, який відображає короткі назви з сильними іменами. У бібліотеці, яку я переглянув, майже 750 записів.

Оновлення: Тепер, коли PowerShell Core 6.0 є відкритим кодом. У цій версії ви можете пропустити вищезазначені кроки і побачити код безпосередньо онлайн у своєму сховищі GitHub . Я не можу гарантувати, що цей код відповідає будь-якій іншій версії PowerShell.


Третє питання без відповіді: що робити, якщо я не хочу вимагати конкретної версії?
jpmc26

1
@ jpmc26 Ну, ви можете просто використовувати Add-Typeабо LoadWithPartialName(), але вам потрібно пам’ятати, що перша не буде на 100% послідовною у всіх версіях, а остання є застарілим методом. Іншими словами, .Net хоче, щоб ви дбали про завантажену вами версію бібліотеки.
Бейкони,

@BaconBits Повна відповідь на питання jpmc26 полягає в тому, що залежно від того, ви перебуваєте на PowerShell 5 або PowerShell 6, завантажена збірка може бути різною. JSON.NET має цю проблему з функціями PS Azure.
Іван Заброський

@BaconBits Це дійсно фантастичне глибоке занурення в PowerShell. Вам слід написати книгу.
Іван Заброський

1
@KolobCanyon Тому що в такому випадку ви, як правило, використовуєте Add-Type -Path, що є другим згаданим кодом або Assembly.LoadFrom()який вирішує залежність для вас (і, наскільки я можу сказати, те, що Add-Type -Pathвикористовує). Єдиний час, який ви повинні використовувати, Assembly.LoadFile()- це якщо вам потрібно завантажити кілька збірок, які мають однакову ідентичність, але різні контури. Це дивна ситуація.
бекон

23

Якщо ви хочете завантажити збірку, не блокуючи її протягом тривалості сеансу PowerShell , використовуйте це:

$bytes = [System.IO.File]::ReadAllBytes($storageAssemblyPath)
[System.Reflection.Assembly]::Load($bytes)

Де $storageAssemblyPathпроходить шлях вашої збірки.

Це особливо корисно, якщо вам потрібно очистити ресурси на вашому сеансі. Наприклад, у сценарії розгортання.


1
👍 👍 👍 Фантастичний. Тому що в Visual Studio при налагодженні Powershell сеанс PS зависає після виконання (через PowerShellToolsProcessHost). Цей підхід це фіксує. Дякую.
CJBS

15

Ось кілька публікацій блогу з численними прикладами способів завантаження збірок у PowerShell v1, v2 та v3.

Способи включають:

  • динамічно з вихідного файлу
  • динамічно від збірки
  • використання інших типів коду, тобто F #

v1.0 Як завантажувати .NET збори в сесії PowerShell
v2.0 Використання коду CSharp (C #) у сценаріях PowerShell 2.0
v3.0 Використання .NET Framework зборів у Windows PowerShell


10

Ви можете завантажити цілу * .dll збірку

$Assembly = [System.Reflection.Assembly]::LoadFrom("C:\folder\file.dll");

3

Жодна з відповідей мені не допомогла, тому я розміщую рішення, яке працювало на мене, все, що мені потрібно було зробити, це імпортувати модуль SQLPS, я зрозумів це, коли випадково запустив команду Restore-SqlDatabase і почав працювати, тобто збірка посилалася в тому модулі якось.

Просто запустіть:

Import-module SQLPS

Примітка. Дякую Джейсону, що зазначив, що SQLPS застарілий

замість запуску:

Import-Module SqlServer

або

Install-Module SqlServer

2
Для тих, хто використовує такий підхід, колишній фінансовий збір, який sqlpsзастаріло на користь модуляsqlserver
Джейсон


2

Ви можете використовувати LoadWithPartialName. Однак це, як вони сказали, застаріло.

Ви дійсно можете піти разом з Add-Typeіншими відповідями, якщо ви не хочете вказувати повний шлях файлу .dll, ви можете просто зробити:

Add-Type -AssemblyName "Microsoft.SqlServer.Management.SMO"

Мені це повернуло помилку, оскільки у мене не встановлено SQL Server (я думаю), однак, з цією ж ідеєю я зміг завантажити збірку Windows Forms:

Add-Type -AssemblyName "System.Windows.Forms"

Ви можете дізнатися точну назву збірки, що належить до певного класу, на сайті MSDN:

Приклад з’ясування назви збірки, що належить до певного класу


2

Переконайтеся, що у вас нижче встановлені функції в порядку

  1. Типи CLR Microsoft System для SQL Server
  2. Об'єкти спільного управління Microsoft SQL Server
  3. Розширення Microsoft Windows PowerShell

Також вам може знадобитися завантаження

Add-Type -Path "C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.Smo.dll"
Add-Type -Path "C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.SqlWmiManagement.dll"

Я провів тиждень, намагаючись завантажити збірку, і не побачив жодного результату з заяви, яка завантажила їх, але коли я спробував це використати, я отримав помилку. Коли я встановив ці три речі, це спрацювало. - дякую
pparas

0

Додайте посилання на збірку вгорі.

#Load the required assemblies SMO and SmoExtended.
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoExtended") | Out-Null

ви могли б зробити з цього приклад?
endo.anaconda

1
Вам просто потрібно додати його на початку скрипту повноважень. Наприклад: Створення резервної копії бази даних: [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.SMO") | Out-Null [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.SmoExtended") | Out-Null $ SQLServer = Read-Host -Prompt 'Ім'я сервера SQL (необов'язково)' IF ([рядок] :: IsNullOrWhitespace ($ SQLServer)) {$ SQLServer = "XXX";} $ SQLDBName = Read-Host -Prompt ' Назва бази даних SQL (необов’язково) 'IF ([рядок] :: IsNullOrWhitespace ($ SQLDBName)) {$ SQLDBName = "XXX";} $ SQLLogin = Read-Host -Prompt' Login "
Амріта Басу
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.