Làm cách nào để tải các cụm trong PowerShell?


153

Mã PowerShell sau đây

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

Đưa ra thông báo lỗi sau:

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

Mỗi câu trả lời trên Internet đều viết rằng tôi phải tải lắp ráp - chắc chắn tôi có thể đọc được từ thông báo lỗi :-) - câu hỏi là:

Làm thế nào để bạn tải lắp ráp và làm cho kịch bản hoạt động?

Câu trả lời:


179

LoadWithPartialNameđã bị phản đối Giải pháp được đề xuất cho PowerShell V3 là sử dụng Add-Typelệnh ghép ngắn, ví dụ:

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

Có nhiều phiên bản khác nhau và bạn có thể muốn chọn một phiên bản cụ thể. :-)


1
Được rồi tôi sử dụng PowerShell3 - những lệnh này bao gồm rất phức tạp. Tôi chỉ mong đợi một cái gì đó như "bao gồm tên tệp".
Baxter

6
PowerShell không phân biệt chữ hoa chữ thường (trừ khi bạn bảo nó phân biệt chữ hoa chữ thường với các toán tử như -cmatch, -ceq). Vì vậy, vỏ trên tên lệnh và tham số không quan trọng.
Keith Hill

5
Đúng. msdn.microsoft.com/en-us/l Library / 12xc5368 (v = vs.110) .aspx Xem ghi chú ở trên cùng - This API is now obsolete. Tất nhiên, điều đó không ngăn mọi người sử dụng nó.
Keith Hill

2
Mặc dù đúng về mặt kỹ thuật LoadWithPartialNameđã bị phản đối, nhưng những lý do (như được nêu trong blog.msdn.com/b/suzcook/archive/2003/05/30/57159.aspx ) rõ ràng không áp dụng cho phiên Powershell tương tác. Tôi đề nghị bạn thêm một lưu ý rằng API phù hợp với việc sử dụng Powershell tương tác.
Micha Wiedenmann

Hầu hết thời gian, tôi không có vấn đề gì với hội đồng SMO nhưng đôi khi tôi cần phải giết powershell và khi tôi làm, tôi bắt đầu gặp vấn đề về tải SMO. Thêm add-type -Path sửa lỗi đó.
Nicolas de Fontenay

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

8
Điều này là quá hữu ích để được phản đối mà không cần thay thế! Nhóm của tôi sử dụng hỗn hợp các công cụ máy khách 2008 và 2012. Đây là cách duy nhất để làm cho các kịch bản PowerShell của tôi hoạt động cho tất cả nhóm của tôi mà không bao gồm logic dự phòng phiên bản vụng về.
Iain Samuel McLean Elder

4
Bạn có thể chuyển đầu ra sang Out-Nullnếu bạn không muốn GAC lặp lại nội dung.
Iain Samuel McLean Elder

3
@Baxter - bạn nên chấp nhận câu trả lời này hoặc của Keith và để câu hỏi này được đánh dấu trả lời.
Jaykul

3
Tôi sử dụng [void] [System.Reflection.Assugging] :: LoadWithPartialName ("Microsoft.SqlServer.Smo")
Soeren L. Nielsen

@IainElder "logic vụng về phiên bản vụng về" Bạn nói điều đó cho đến khi bạn chạy vào phiên bản không tương thích! Điều đó không khó nói Add-Type -Path [...]; if (!$?) { Add-Type -Path [...] } elseif [...].
Bacon Bits

44

Hầu hết mọi người đều biết rằng điều đó System.Reflection.Assembly.LoadWithPartialNamekhông được chấp nhận, nhưng hóa ra điều Add-Type -AssemblyName Microsoft.VisualBasic đó không hành xử tốt hơn nhiều so vớiLoadWithPartialName :

Thay vì thực hiện bất kỳ nỗ lực nào để phân tích yêu cầu của bạn trong ngữ cảnh hệ thống của bạn, [Loại bổ sung] nhìn vào một bảng nội bộ tĩnh để dịch "tên một phần" thành "tên đầy đủ".

Nếu "tên một phần" của bạn không xuất hiện trong bảng của họ, tập lệnh của bạn sẽ thất bại.

Nếu bạn có nhiều phiên bản lắp ráp được cài đặt trên máy tính của bạn, không có thuật toán thông minh nào để chọn giữa chúng. Bạn sẽ nhận được bất cứ cái nào xuất hiện trong bảng của họ, có thể là cái cũ hơn, lỗi thời.

Nếu các phiên bản bạn đã cài đặt hoàn toàn mới hơn phiên bản lỗi thời trong bảng, tập lệnh của bạn sẽ thất bại.

Add-Type không có trình phân tích cú pháp thông minh của "tên một phần" như thế nào .LoadWithPartialNames.

Những gì Microsoft nói bạn thực sự cần phải làm là một cái gì đó như thế này:

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

Hoặc, nếu bạn biết đường dẫn, một cái gì đó như thế này:

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

Tên dài được đặt cho lắp ráp được gọi là tên mạnh , cả hai đều là duy nhất cho phiên bản và lắp ráp, và đôi khi còn được gọi là tên đầy đủ.

Nhưng điều này để lại một vài câu hỏi chưa được trả lời:

  1. Làm cách nào để xác định tên mạnh của những gì thực sự được tải trên hệ thống của tôi với một phần tên nhất định?

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

Chúng cũng nên hoạt động:

Add-Type -AssemblyName $TypeName -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. Nếu tôi muốn tập lệnh của mình luôn sử dụng một phiên bản cụ thể của một tệp dll nhưng tôi không thể chắc chắn về nơi nó được cài đặt, làm cách nào để xác định tên mạnh là gì từ dll?

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

Hoặc là:

Add-Type $Path -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. Nếu tôi biết tên mạnh, làm cách nào để xác định đường dẫn dll?

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

  2. Và, trên một mạch tương tự, nếu tôi biết tên loại của những gì tôi đang sử dụng, làm thế nào để tôi biết nó được lắp ráp từ đâu?

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

  3. Làm thế nào để tôi thấy những gì lắp ráp có sẵn?

Tôi đề nghị mô-đun GAC PowerShell . Get-GacAssembly -Name 'Microsoft.SqlServer.Smo*' | Select Name, Version, FullNamehoạt động khá tốt.

  1. Làm thế nào tôi có thể xem danh sách Add-Typesử dụng?

Điều này phức tạp hơn một chút. Tôi có thể mô tả cách truy cập nó cho bất kỳ phiên bản PowerShell nào bằng bộ phản xạ .Net (xem bản cập nhật bên dưới cho PowerShell Core 6.0).

Đầu tiên, hãy tìm ra thư viện nào Add-Typeđến từ:

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

Mở DLL kết quả với phản xạ của bạn. Tôi đã sử dụng ILSpy cho việc này vì nó là FLOSS, nhưng bất kỳ bộ phản xạ C # nào cũng hoạt động. Mở thư viện đó và nhìn vào Microsoft.Powershell.Commands.Utility. Dưới Microsoft.Powershell.Commands, nên có AddTypeCommand.

Trong danh sách mã cho điều đó, có một lớp riêng , InitializeStrongNameDictionary(). Đó là danh sách từ điển ánh xạ các tên ngắn thành tên mạnh. Có gần 750 mục trong thư viện tôi đã xem.

Cập nhật: Bây giờ PowerShell Core 6.0 là nguồn mở. Đối với phiên bản đó, bạn có thể bỏ qua các bước trên và xem mã trực tuyến trong kho lưu trữ GitHub của họ . Tuy nhiên, tôi không thể đảm bảo rằng mã đó phù hợp với bất kỳ phiên bản PowerShell nào khác.


Câu hỏi chưa được trả lời thứ 3: nếu tôi không muốn yêu cầu một phiên bản cụ thể thì sao?
jpmc26

1
@ jpmc26 Chà, bạn chỉ có thể sử dụng Add-Typehoặc LoadWithPartialName(), nhưng bạn cần lưu ý rằng cái trước sẽ không nhất quán 100% giữa các phiên bản và cái sau là một phương pháp lỗi thời. Nói cách khác, .Net muốn bạn quan tâm đến phiên bản của thư viện mà bạn tải.
Bacon Bits

@BaconBits Câu trả lời đầy đủ cho câu hỏi của jpmc26 là, tùy thuộc vào việc bạn sử dụng PowerShell 5 hay PowerShell 6, lắp ráp được tải có thể khác nhau. JSON.NET có vấn đề này với các hàm Azure PS.
John Zabroski

@BaconBits Đây là một lần lặn sâu thực sự tuyệt vời vào PowerShell. Bạn nên viết một cuốn sách.
John Zabroski

1
@KolobCanyon Bởi vì trong trường hợp đó, bạn thường nên sử dụng Add-Type -Path, đó là mã thứ hai được đề cập hoặc Assembly.LoadFrom()giải quyết các phụ thuộc cho bạn (và, theo như tôi có thể nói, là những gì Add-Type -Pathsử dụng). Thời gian duy nhất bạn nên sử dụng Assembly.LoadFile()là nếu bạn cần tải nhiều cụm có cùng danh tính nhưng đường dẫn khác nhau. Đó là một tình huống kỳ lạ.
Bacon Bits

23

Nếu bạn muốn tải một cụm mà không khóa nó trong suốt thời gian của phiên PowerShell , hãy sử dụng:

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

Đâu $storageAssemblyPathlà filepath của hội đồng của bạn.

Điều này đặc biệt hữu ích nếu bạn cần dọn sạch các nguồn tài nguyên trong phiên của mình. Ví dụ trong một kịch bản triển khai.


1
👍 Tuyệt vời. Bởi vì trong Visual Studio, khi gỡ lỗi Powershell, phiên PS bị treo xung quanh sau khi thực thi (thông qua PowerShellToolsProcesshost). Cách tiếp cận này khắc phục điều đó. Cảm ơn.
CJBS

15

Dưới đây là một số bài đăng trên blog với nhiều ví dụ về cách tải các cụm trong PowerShell v1, v2 và v3.

Các cách bao gồm:

  • động từ một tệp nguồn
  • năng động từ một hội đồng
  • sử dụng các loại mã khác, tức là F #

v1.0 Cách tải các tập hợp .NET trong phiên PowerShell
v2.0 bằng mã CSharp (C #) trong tập lệnh PowerShell 2.0
v3.0 Sử dụng tập hợp .NET Framework trong Windows PowerShell


10

Bạn có thể tải toàn bộ * lắp ráp với

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

3

Không có câu trả lời nào giúp tôi, vì vậy tôi đang đăng giải pháp hiệu quả cho mình, tất cả những gì tôi phải làm là nhập mô-đun SQLPS, tôi nhận ra điều này khi tình cờ tôi chạy lệnh Restore-SqlDatabase và bắt đầu làm việc, nghĩa là lắp ráp đã được tham chiếu trong mô-đun đó bằng cách nào đó.

Chỉ cần chạy:

Import-module SQLPS

Lưu ý: Cảm ơn Jason vì đã lưu ý rằng SQLPS không được dùng nữa

thay vì chạy:

Import-Module SqlServer

hoặc là

Install-Module SqlServer

2
Đối với bất kỳ ai sử dụng phương pháp này, FYI sqlpskhông được ủng hộ mô-đunsqlserver
Jason

2

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") đã làm cho tôi.


2

Bạn có thể sử dụng LoadWithPartialName. Tuy nhiên, điều đó bị phản đối như họ nói.

Bạn thực sự có thể đi cùng Add-Typevà ngoài các câu trả lời khác, nếu bạn không muốn chỉ định đường dẫn đầy đủ của tệp dll, bạn chỉ cần làm:

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

Đối với tôi, điều này đã trả về một lỗi, vì tôi chưa cài đặt SQL Server (tôi đoán vậy), tuy nhiên, với cùng ý tưởng này, tôi đã có thể tải cụm Windows Forms:

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

Bạn có thể tìm ra tên lắp ráp chính xác thuộc về lớp cụ thể trên trang MSDN:

Ví dụ về việc tìm ra tên lắp ráp thuộc về một lớp cụ thể


2

Đảm bảo rằng bạn có các tính năng bên dưới được cài đặt theo thứ tự

  1. Các loại CLR hệ thống của Microsoft cho SQL Server
  2. Đối tượng quản lý dùng chung của Microsoft SQL Server
  3. Tiện ích mở rộng Microsoft Windows PowerShell

Ngoài ra, bạn có thể cần phải tải

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"

Tôi đã dành một tuần để tải lắp ráp và không thấy bất kỳ đầu ra nào từ câu lệnh đã tải chúng nhưng khi tôi cố gắng sử dụng nó, tôi đã gặp lỗi. Khi tôi cài đặt ba thứ này, nó đã hoạt động. - cảm ơn
pparas

0

Thêm các tài liệu tham khảo lắp ráp ở đầu.

#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

bạn có thể làm một ví dụ về nó?
endo.anaconda

1
Bạn chỉ cần thêm nó vào đầu tập lệnh powershell. Ví dụ: Tạo Sao lưu cơ sở dữ liệu: [System.Reflection.Assugging] :: LoadWithPartialName ("Microsoft.SqlServer.SMO") | Out-Null [System.Reflection.Assugging] :: LoadWithPartialName ("Microsoft.SqlServer.SmoExtends") | Out-Null $ SQLServer = Read-Host -Prompt 'SQL Server name (tùy chọn)' IF ([chuỗi] :: IsNullOrWhitespace ($ SQLServer)) {$ SQLServer = "XXX";} $ SQLDBName = Read-Host -Prompt ' Tên cơ sở dữ liệu SQL (tùy chọn) 'IF ([chuỗi] :: IsNullOrWhitespace ($ SQLDBName)) {$ SQLDBName = "XXX";} $ SQLLogin = Read-Host -Prompt' Đăng nhập '
Amrita Basu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.