Jak ładować zestawy w PowerShell?


153

Poniższy kod programu PowerShell

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

Daje następujący komunikat o błędzie:

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

Każda odpowiedź w internecie pisze, że muszę załadować montaż - na pewno mogę to wyczytać z komunikatu błędu :-) - pytanie brzmi:

Jak ładujesz assembler i uruchamiasz skrypt?

Odpowiedzi:


179

LoadWithPartialNamezostał wycofany. Zalecanym rozwiązaniem dla PowerShell V3 jest użycie polecenia Add-Typecmdlet np:

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

Istnieje wiele różnych wersji i możesz wybrać konkretną wersję. :-)


1
Dobra, używam PowerShell3 - te polecenia obejmują wydają się bardzo skomplikowane. Spodziewałbym się po prostu czegoś w rodzaju „dołącz nazwę pliku”.
— Baxter,

6
W programie PowerShell nie jest rozróżniana wielkość liter (chyba że w przypadku operatorów takich jak -cmatch, -ceq rozróżniasz wielkość liter). Zatem wielkość liter w nazwach poleceń i parametrach nie ma znaczenia.
— Keith Hill

5
Tak. msdn.microsoft.com/en-us/library/12xc5368(v=vs.110).aspx Zobacz uwagę u góry - This API is now obsolete. Oczywiście to nie powstrzymuje ludzi przed używaniem go.
— Keith Hill

2
Chociaż jest to technicznie poprawne rozwiązanie, które LoadWithPartialNamezostało wycofane, przyczyny (opisane w blogs.msdn.com/b/suzcook/archive/2003/05/30/57159.aspx ) wyraźnie nie dotyczą interaktywnej sesji programu Powershell. Proponuję dodać uwagę, że API jest dobre do interaktywnego użycia Powershell.
— Micha Wiedenmann

Przez większość czasu nie mam problemu z montażem SMO, ale czasami muszę wyłączyć PowerShell, a kiedy to zrobię, zaczynam mieć problemy z ładowaniem SMO. Dodanie add-type -Path rozwiązuje ten problem.
— Nicolas de Fontenay,

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

8
Jest to zbyt przydatne, aby wycofać je bez wymiany! Mój zespół używa różnych narzędzi klienckich z 2008 i 2012 roku. Jest to jedyny sposób, aby moje skrypty PowerShell działały dla całego mojego zespołu bez uwzględnienia niezgrabnej logiki zastępczej wersji.
— Iain Samuel McLean Elder

4
Możesz potokować wyjście, Out-Nulljeśli nie chcesz, aby GAC odbijał echo.
— Iain Samuel McLean Elder

3
@Baxter - powinieneś zaakceptować tę odpowiedź lub odpowiedź Keitha i pozwolić na zaznaczenie odpowiedzi na to pytanie.
— Jaykul

3
Używam [void] [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.Smo")
— Soeren L. Nielsen

@IainElder "niezdarna logika awaryjna wersji" Mówisz tak, dopóki nie napotkasz niezgodności wersji! Nie jest trudno powiedzieć Add-Type -Path [...]; if (!$?) { Add-Type -Path [...] } elseif [...].
— Bacon Bits

44

Większość ludzi już wie, że System.Reflection.Assembly.LoadWithPartialNamejest to przestarzałe, ale okazuje się, że Add-Type -AssemblyName Microsoft.VisualBasic nie zachowuje się dużo lepiej niżLoadWithPartialName :

Zamiast próbować analizować żądanie w kontekście systemu, [Add-Type] sprawdza statyczną, wewnętrzną tabelę, aby przetłumaczyć „częściową nazwę” na „pełną nazwę”.

Jeśli Twoja „częściowa nazwa” nie pojawi się w ich tabeli, skrypt zawiedzie.

Jeśli masz wiele wersji zestawu zainstalowanych na komputerze, nie ma inteligentnego algorytmu do wyboru między nimi. Otrzymasz dowolny, który pojawi się w ich tabeli, prawdopodobnie starszy, nieaktualny.

Jeśli wszystkie zainstalowane wersje są nowsze niż przestarzała w tabeli, skrypt nie powiedzie się.

Add-Type nie ma inteligentnego analizatora składni „nazw częściowych”, takich jak .LoadWithPartialNames.

Microsoft twierdzi, że faktycznie powinieneś zrobić coś takiego:

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

Lub, jeśli znasz ścieżkę, coś takiego:

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

Ta długa nazwa nadana zespołowi jest znana jako silna nazwa , która jest unikalna zarówno dla wersji, jak i zestawu, a czasami jest również nazywana pełną nazwą.

Ale to pozostawia kilka pytań bez odpowiedzi:

  1. Jak określić silną nazwę tego, co faktycznie jest ładowane w moim systemie z podaną nazwą częściową?

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

Te też powinny działać:

Add-Type -AssemblyName $TypeName -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. Jeśli chcę, aby mój skrypt zawsze korzystał z określonej wersji pliku .dll, ale nie mam pewności, gdzie jest zainstalowany, w jaki sposób mogę określić, jaka jest silna nazwa z pliku .dll?

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

Lub:

Add-Type $Path -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. Jeśli znam silną nazwę, jak określić ścieżkę .dll?

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

  2. I w podobny sposób, jeśli znam nazwę typu tego, czego używam, skąd mam wiedzieć, z jakiego zespołu pochodzi?

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

  3. Jak mogę sprawdzić, jakie zestawy są dostępne?

Proponuję moduł GAC PowerShell . Get-GacAssembly -Name 'Microsoft.SqlServer.Smo*' | Select Name, Version, FullNamedziała całkiem nieźle.

  1. Jak mogę zobaczyć listę, która Add-Typeużywa?

To jest trochę bardziej złożone. Mogę opisać, jak uzyskać do niego dostęp dla dowolnej wersji programu PowerShell z reflektorem .Net (zobacz aktualizację poniżej dotyczącą programu PowerShell Core 6.0).

Najpierw dowiedz się, z której biblioteki Add-Typepochodzi:

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

Otwórz wynikową bibliotekę DLL za pomocą reflektora. Użyłem ILSpy za to, bo to FLOSS, ale wszelkie C # reflektor powinien działać. Otwórz tę bibliotekę i zajrzyj do środka Microsoft.Powershell.Commands.Utility. Pod Microsoft.Powershell.Commandsspodem powinno być AddTypeCommand.

W kodzie witryny, że znajduje się prywatny klasy InitializeStrongNameDictionary(). Zawiera słownik, który odwzorowuje krótkie nazwy na silne nazwy. W bibliotece, którą przeglądałem, jest prawie 750 pozycji.

Aktualizacja: teraz, gdy program PowerShell Core 6.0 jest oprogramowaniem typu open source. W przypadku tej wersji możesz pominąć powyższe kroki i zobaczyć kod bezpośrednio online w ich repozytorium GitHub . Nie mogę jednak zagwarantować, że ten kod pasuje do dowolnej innej wersji programu PowerShell.


Trzecie pytanie bez odpowiedzi: co jeśli nie chcę wymagać określonej wersji?
— jpmc26

1
@ jpmc26 Cóż, możesz po prostu użyć Add-Typelub LoadWithPartialName(), ale musisz mieć świadomość, że ta pierwsza nie będzie w 100% spójna we wszystkich wersjach, a druga jest przestarzałą metodą. Innymi słowy, .Net chce, abyś dbał o wersję biblioteki, którą ładujesz.
— Bacon Bits

@BaconBits Pełna odpowiedź na pytanie jpmc26 jest taka, że ​​w zależności od tego, czy korzystasz z PowerShell 5 czy PowerShell 6, załadowany zestaw może być inny. JSON.NET ma ten problem z funkcjami Azure PS.
— John Zabroski

@BaconBits To naprawdę fantastyczne, głębokie zagłębienie się w PowerShell. Powinieneś napisać książkę.
— John Zabroski

1
@KolobCanyon Ponieważ w takim przypadku powinieneś generalnie użyć Add-Type -Path, który jest drugim wymienionym kodem lub Assembly.LoadFrom()który rozwiązuje za Ciebie zależności (i, o ile wiem, tego Add-Type -Pathużywa). Jedyny przypadek, którego powinieneś używać, Assembly.LoadFile()to ładowanie wielu zestawów, które mają tę samą tożsamość, ale różne ścieżki. To dziwna sytuacja.
— Bacon Bits

23

Jeśli chcesz załadować zestaw bez blokowania go podczas trwania sesji programu PowerShell , użyj tego:

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

Gdzie $storageAssemblyPathjest ścieżka pliku twojego zespołu.

Jest to szczególnie przydatne, jeśli chcesz wyczyścić zasoby podczas sesji. Na przykład w skrypcie wdrażania.


1
👍 👍 👍 Fantastycznie. Ponieważ w programie Visual Studio podczas debugowania programu Powershell sesja PS zawiesza się po wykonaniu (za pośrednictwem PowerShellToolsProcessHost). Takie podejście rozwiązuje ten problem. Dzięki.
— CJBS


10

Możesz załadować cały zestaw * .dll za pomocą

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

3

Żadna z odpowiedzi mi nie pomogła, więc publikuję rozwiązanie, które działało dla mnie, wystarczyło zaimportować moduł SQLPS, zdałem sobie z tego sprawę, gdy przypadkowo uruchomiłem polecenie Restore-SqlDatabase i zacząłem działać, co oznacza, że zestaw był w jakiś sposób przywoływany w tym module.

Po prostu biegnij:

Import-module SQLPS

Uwaga: dziękujemy Jasonowi za zauważenie, że SQLPS jest przestarzały

zamiast tego biegnij:

Import-Module SqlServer

lub

Install-Module SqlServer

2
Dla każdego, kto korzysta z tego podejścia, informacja o Twojej sqlpssqlserver
— Jason

2

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") pracował dla mnie.


2

Możesz użyć LoadWithPartialName. Jednak, jak powiedzieli, jest to przestarzałe.

Rzeczywiście możesz się zgodzić Add-Typei oprócz innych odpowiedzi, jeśli nie chcesz określać pełnej ścieżki do pliku .dll, możesz po prostu zrobić:

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

Mnie zwróciło to błąd, ponieważ nie mam zainstalowanego SQL Server (chyba), jednak z tym samym pomysłem udało mi się załadować zestaw Windows Forms:

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

Dokładną nazwę zestawu należącego do określonej klasy można znaleźć w witrynie MSDN:

Przykład znalezienia nazwy zestawu należącego do określonej klasy


2

Upewnij się, że poniższe funkcje są zainstalowane w kolejności

  1. Typy środowiska CLR systemu Microsoft dla programu SQL Server
  2. Obiekty zarządzania współużytkowanego Microsoft SQL Server
  3. Rozszerzenia Microsoft Windows PowerShell

Konieczne może być również załadowanie

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"

Spędziłem tydzień próbując załadować zestaw i nie widziałem żadnych danych wyjściowych z instrukcji, która je załadowała, ale kiedy próbowałem go użyć, pojawił się błąd. Kiedy zainstalowałem te trzy rzeczy, zadziałało. - dzięki
— pparas

0

Dodaj odniesienia do zestawów u góry.

#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

czy mógłbyś zrobić z tego przykład?
— endo.anaconda

1
Wystarczy dodać go na początku skryptu PowerShell. Na przykład: Utwórz kopię zapasową bazy danych: [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.SMO") | Out-Null [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.SmoExtended") | Out-Null $ SQLServer = Read-Host -Prompt 'Nazwa SQL Server (opcjonalnie)' IF ([string] :: IsNullOrWhitespace ($ SQLServer)) {$ SQLServer = "XXX";} $ SQLDBName = Read-Host -Prompt ' Nazwa bazy danych SQL (opcjonalnie) 'IF ([string] :: IsNullOrWhitespace ($ SQLDBName)) {$ SQLDBName = "XXX";} $ SQLLogin = Read-Host -Prompt' Login '
— Amrita Basu
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.