PowerShell'de derlemeler nasıl yüklenir?


155

Aşağıdaki PowerShell kodu

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

Aşağıdaki hata mesajını verir:

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

İnternetteki her cevap, derlemeyi yüklemem gerektiğini yazıyor - tabii ki bunu hata mesajından okuyabilirim :-) - soru şudur:

Montajı nasıl yükler ve betiğin çalışmasını sağlarsınız?

Yanıtlar:


180

LoadWithPartialNamekullanımdan kaldırıldı. PowerShell V3 için önerilen çözüm Add-Typecmdlet'i kullanmaktır, örneğin:

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

Birden çok farklı sürüm var ve belirli bir sürümü seçmek isteyebilirsiniz. :-)


1
Tamam, PowerShell3 kullanıyorum - bunlar içerme komutları çok karmaşık görünüyor. "Dosya adını dahil et" gibi bir şey beklerdim.
Baxter

6
PowerShell büyük / küçük harfe duyarlıdır (-cmatch, -ceq gibi operatörlerle büyük / küçük harfe duyarlı olmasını söylemediğiniz sürece). Yani komut isimleri ve parametrelerindeki büyük / küçük harf durumu önemli değil.
Keith Tepesi

5
Evet. msdn.microsoft.com/en-us/library/12xc5368(v=vs.110).aspx Üstteki nota bakın - This API is now obsolete. Elbette bu, insanların onu kullanmasını engellemez.
Keith Hill

2
Kullanımdan LoadWithPartialNamekaldırılan teknik olarak doğru olsa da, nedenler ( blogs.msdn.com/b/suzcook/archive/2003/05/30/57159.aspx'de belirtildiği gibi ) etkileşimli bir Powershell oturumu için açıkça geçerli değildir. API'nin etkileşimli Powershell kullanımı için uygun olduğuna bir not eklemenizi öneririm.
Micha Wiedenmann

Çoğu zaman, SMO montajıyla ilgili bir sorunum yok ama bazen powershell'i öldürmem gerekiyor ve yaptığımda SMO yükleme sorunları yaşamaya başlıyorum. Add-type -Path eklemek bunu düzeltir.
Nicolas de Fontenay

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

8
Bu, değiştirilmeden kullanımdan kaldırılamayacak kadar yararlıdır! Ekibim, 2008 ve 2012 istemci araçlarının bir karışımını kullanıyor. Bu, PowerShell komut dosyalarımı beceriksiz sürüm-geri dönüş mantığı dahil etmeden tüm ekibim için çalıştırmanın tek yoludur.
Iain Samuel McLean Elder

4
Out-NullGAC'nin bazı şeyleri yankılamasını istemiyorsanız , çıktıyı yöneltebilirsiniz.
Iain Samuel McLean Elder

3
@Baxter - bu yanıtı veya Keith'i kabul etmeli ve bu sorunun yanıtlanmış olarak işaretlenmesine izin vermelisiniz.
Jaykul

3
[Void] [System.Reflection.Assembly] kullanıyorum :: LoadWithPartialName ("Microsoft.SqlServer.Smo")
Soeren L. Nielsen

@IainElder "beceriksiz sürüm-geri dönüş mantığı" Sürüm uyumsuzluğu ile karşılaşana kadar bunu söylüyorsunuz! Söylemesi o kadar da zor değil Add-Type -Path [...]; if (!$?) { Add-Type -Path [...] } elseif [...].
Bacon Bits

46

Şimdiye kadar çoğu insan bunun System.Reflection.Assembly.LoadWithPartialNamekullanımdan kaldırıldığını biliyor , ancak bunun Add-Type -AssemblyName Microsoft.VisualBasic şunlardan daha iyi davranmadığıLoadWithPartialName ortaya çıktı :

[Add-Type], isteğinizi sisteminizin bağlamında ayrıştırmak yerine, "kısmi adı" "tam ada" çevirmek için statik, dahili bir tabloya bakar.

"Kısmi adınız" onların tablosunda görünmüyorsa, komut dosyanız başarısız olacaktır.

Bilgisayarınızda montajın birden çok sürümü kuruluysa, aralarında seçim yapabileceğiniz akıllı bir algoritma yoktur. Masalarında hangisi görünüyorsa, muhtemelen daha eski, modası geçmiş olanı alacaksınız.

Yüklediğiniz sürümlerin tümü tablodaki eski sürümden daha yeniyse, betiğiniz başarısız olacaktır.

Add-Type, "kısmi adlar" gibi akıllı ayrıştırıcıya sahip değildir .LoadWithPartialNames.

Microsoft'un .Net ekiplerinin aslında yapmanız gerektiğini söylediği şey şuna benzer:

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

Veya yolu biliyorsanız, bunun gibi bir şey:

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

Meclis için verilen bu uzun isim, güçlü isim olarak bilinir. , hem sürüme hem de derlemeye özgü olan olarak bilinir ve bazen tam ad olarak da bilinir.

Ancak bu, birkaç soruyu cevapsız bırakıyor:

  1. Belirli bir kısmi adla sistemime gerçekte yüklenen şeyin güçlü adını nasıl belirleyebilirim?

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

Bunlar da çalışmalıdır:

Add-Type -AssemblyName $TypeName -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. Komut dosyamın her zaman bir .dll'nin belirli bir sürümünü kullanmasını istiyorsam, ancak nereye yüklendiğinden emin olamıyorsam, güçlü adın .dll'den ne olduğunu nasıl belirleyebilirim?

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

Veya:

Add-Type $Path -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. Güçlü adı biliyorsanız, .dll yolunu nasıl belirleyebilirim?

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

  2. Ve benzer bir şekilde, kullandığım şeyin tip adını bilirsem, hangi montajdan geldiğini nasıl bilebilirim?

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

  3. Hangi montajların mevcut olduğunu nasıl görebilirim?

GAC PowerShell modülünü öneririm . Get-GacAssembly -Name 'Microsoft.SqlServer.Smo*' | Select Name, Version, FullNameoldukça iyi çalışıyor.

  1. Kullanılan listeyi nasıl görebilirim Add-Type?

Bu biraz daha karmaşık. Net reflektörlü herhangi bir PowerShell sürümü için buna nasıl erişileceğini açıklayabilirim (PowerShell Core 6.0 için aşağıdaki güncellemeye bakın).

İlk olarak, hangi kitaplığın Add-Typegeldiğini bulun:

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

Ortaya çıkan DLL dosyasını reflektörünüzle açın. Bunun için ILSpy'ı kullandım çünkü FLOSS, ancak herhangi bir C # reflektörü çalışmalı. O kitaplığı açın ve içeri bakın Microsoft.Powershell.Commands.Utility. Altında Microsoft.Powershell.Commandsolmalı AddTypeCommand.

Bunun için kod listesinde özel bir sınıf var InitializeStrongNameDictionary(). Bu, kısa adları güçlü adlarla eşleyen sözlüğü listeler. Baktığım kütüphanede neredeyse 750 kayıt var.

Güncelleme: PowerShell Core 6.0 artık açık kaynak. Bu sürüm için, yukarıdaki adımları atlayabilir ve kodu doğrudan GitHub deposunda çevrimiçi olarak görebilirsiniz . Bununla birlikte, bu kodun başka herhangi bir PowerShell sürümüyle eşleşeceğini garanti edemem.

Güncelleme 2: Powershell 7+ artık hash tablosu aramasına sahip görünmüyor. Bunun yerine , yorumların LoadWithPartialName'e "mümkün olan en yakın yaklaşım" adını verdiği bir LoadAssemblyHelper()yöntem kullanırlar. Temel olarak, bunu yaparlar:

loadedAssembly = Assembly.Load(new AssemblyName(assemblyName));

Şimdi, yorumlar ayrıca "kullanıcılar Add-Type -AssemblyName Forms (System.Windows.Forms yerine) diyebilir" diyor . Ancak, Windows 10 2004'te Powershell v7.0.3'te gördüğüm bu değil.

# Returns an error
Add-Type -AssemblyName Forms

# Returns an error
[System.Reflection.Assembly]::Load([System.Reflection.AssemblyName]::new('Forms'))

# Works fine
Add-Type -AssemblyName System.Windows.Forms

# Works fine
[System.Reflection.Assembly]::Load([System.Reflection.AssemblyName]::new('System.Windows.Forms'))

Yani yorumlar biraz gizemli görünüyor.

Assembly.Load(AssemblyName)Belirtilen sürüm veya genel anahtar belirteci olmadığında mantığın tam olarak ne olduğunu bilmiyorum . Birden çok yüklediyseniz, bu, LoadWithPartialName'in potansiyel olarak derlemenin yanlış sürümünü yüklemeyi sevdiği sorunların çoğuna sahip olmasını beklerim.


3. cevaplanmamış soru: ya belirli bir sürüme ihtiyaç duymak istemiyorsam?
jpmc26

1
@ jpmc26 Sadece Add-Typeveya kullanabilirsiniz LoadWithPartialName(), ancak ilkinin sürümler arasında% 100 tutarlı olmayacağını ve ikincisinin eski bir yöntem olduğunu bilmeniz gerekir. Diğer bir deyişle, .Net, yüklediğiniz kitaplık sürümüyle ilgilenmenizi ister.
Bacon Bits

@BaconBits jpmc26'nın sorusuna tam yanıt, PowerShell 5 veya PowerShell 6'da olmanıza bağlı olarak, yüklenen derlemenin farklı olabileceğidir. JSON.NET, Azure PS işlevlerinde bu sorunu yaşıyor.
John Zabroski

@BaconBits Bu, PowerShell'e gerçekten harika bir derin dalıştır. Kitap yazmalısın.
John Zabroski

1
@KolobCanyon Çünkü bu durumda genel olarak Add-Type -Path, bahsedilen ikinci kod Assembly.LoadFrom()olan veya sizin için bağımlılıkları çözen (ve anlayabildiğim kadarıyla, ne Add-Type -Pathkullanır) kullanmalısınız. Kullanmanız gereken tek zaman Assembly.LoadFile(), aynı kimliğe ancak farklı yollara sahip birden çok derleme yüklemeniz gerekmesidir. Bu garip bir durum.
Bacon Bits

23

PowerShell oturumu süresince bir montajı kilitlemeden yüklemek istiyorsanız , şunu kullanın:

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

$storageAssemblyPathMontajınızın dosya yolu nerede .

Bu, özellikle oturumunuzdaki kaynakları temizlemeniz gerekiyorsa faydalıdır. Örneğin bir dağıtım betiğinde.


1
👍 👍 👍 Harika. Çünkü Visual Studio'da, Powershell'de hata ayıklarken, PS oturumu yürütmeden sonra takılıyor (PowerShellToolsProcessHost aracılığıyla). Bu yaklaşım bunu düzeltir. Teşekkürler.
CJBS


10

Tüm * .dll derlemesini şu şekilde yükleyebilirsiniz:

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

3

Cevapların hiçbiri bana yardımcı olmadı, bu yüzden benim için çalışan çözümü yayınlıyorum, tek yapmam gereken SQLPS modülünü içeri aktarmaktı, bunu kazara Restore-SqlDatabase komutunu çalıştırıp çalışmaya başladığımda fark ettim, yani derleme bir şekilde bu modülde referans alınmıştır.

Sadece koş:

Import-module SQLPS

Not: SQLPS'nin kullanımdan kaldırıldığını belirttiği için teşekkürler Jason

bunun yerine çalıştırın:

Import-Module SqlServer

veya

Install-Module SqlServer

2
Bu yaklaşımı kullanan herkes için sqlps, modül lehine kullanımdan kaldırılan FYIsqlserver
Jason

2

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") benim için çalıştı.


2

Kullanabilirsin LoadWithPartialName. Ancak, dedikleri gibi kullanımdan kaldırıldı.

Gerçekten de devam edebilir Add-Typeve diğer yanıtlara ek olarak, .dll dosyasının tam yolunu belirtmek istemiyorsanız, şunları yapabilirsiniz:

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

Bana göre bu bir hata döndürdü, çünkü yüklü SQL Server'ım yok (sanırım), ancak aynı fikirle Windows Forms derlemesini yükleyebildim:

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

MSDN sitesinde belirli bir sınıfa ait tam montaj adını bulabilirsiniz:

Belirli bir sınıfa ait derleme adını bulma örneği


2

Sırasıyla aşağıdaki özelliklerin kurulu olduğundan emin olun

  1. SQL Server için Microsoft Sistem CLR Türleri
  2. Microsoft SQL Server Paylaşılan Yönetim Nesneleri
  3. Microsoft Windows PowerShell Uzantıları

Ayrıca yüklemeniz gerekebilir

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"

Montajı yüklemeye çalışarak bir hafta geçirdim ve onları yükleyen ifadeden herhangi bir çıktı görmedim ama kullanmaya çalıştığımda hata aldım, bu üç şeyi kurduğumda çalıştı. - teşekkürler
pparas

0

Montaj referanslarını en üste ekleyin.

#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

bundan bir örnek verebilir misin?
endo.anaconda

1
Bunu powershell betiğinin başına eklemeniz yeterlidir. Örneğin: Bir Veritabanının Yedeğini Oluşturun: [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.SMO") | Out-Null [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.SmoExtended") | Out-Null $ SQLServer = Read-Host -Prompt 'SQL Server adı (isteğe bağlı)' IF ([string] :: IsNullOrWhitespace ($ SQLServer)) {$ SQLServer = "XXX";} $ SQLDBName = Okuma-Ana Bilgisayar -Prompt ' SQL Veritabanı adı (isteğe bağlı) 'IF ([string] :: IsNullOrWhitespace ($ SQLDBName)) {$ SQLDBName = "XXX";} $ SQLLogin = Read-Host -Prompt' Login '
Amrita Basu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.