2009-12-22 31 views
23

Có thể sử dụng Dependency Injection (DI) với Windows PowerShell không?Tiêm phụ thuộc với PowerShell

Các thử nghiệm tích cực của tôi cho thấy rằng không phải vậy. Nếu tôi cố gắng sử dụng Constructor Injection trong CmdLet nó thậm chí không tự đăng ký. Nói cách khác, điều này là không thể:

[Cmdlet(VerbsDiagnostic.Test, "Ploeh")] 
public class PloehCmdlet : Cmdlet 
{ 
    public PloehCmdlet(IFoo foo) 
    { 
     if (foo == null) 
     { 
      throw new ArgumentNullException("foo"); 
     } 

     // save foo for later use 
    } 

    protected override void ProcessRecord() 
    { 
     this.WriteObject("Ploeh"); 
    } 
} 

Nếu tôi thêm một constructor mặc định, lệnh có thể được đăng ký và sử dụng, nhưng không có constructor mặc định, nó chỉ đơn giản là không có sẵn.

Tôi biết tôi có thể sử dụng Dịch vụ định vị để truy xuất các phụ thuộc của tôi, nhưng tôi xem xét việc chống mẫu nên không muốn đi theo cách đó.

Tôi đã hy vọng rằng PowerShell API có một số loại móc 'Nhà máy' tương tự như ServiceFactory của WCF, nhưng nếu có, tôi không thể tìm thấy nó.

+0

tôi tò mò là tốt. –

+1

Tôi đang gặp khó khăn trong việc sử dụng tốt trường hợp này. – yfeldblum

+0

Tôi đang sử dụng bộ định vị dịch vụ. :) –

Trả lời

10

Một cá thể của lớp lệnh ghép ngắn được tạo từ một hàm tạo rỗng mỗi khi lệnh ghép ngắn được sử dụng trong PowerShell. Bạn không kiểm soát được nhà xây dựng nào PowerShell sẽ chọn, vì vậy bạn không thể làm những gì bạn đang đề xuất một cách đơn giản (và tôi thực sự có một thời gian khó tưởng tượng tại sao bạn muốn). Vì vậy, câu trả lời đơn giản cho câu hỏi này là KHÔNG.

Để đạt được một ảnh hưởng tương tự, bạn có thể tạo giao diện trông giống như lệnh ghép ngắn (có BeginProcessing/EndProcessing/ProcessRecord/StopProcessing) và sử dụng để đưa một loạt các lệnh ghép ngắn vào mã thực. IMHO này sẽ là một cách tiếp cận quá phức tạp.

Tôi thực sự không thấy lý do tại sao bạn đang cố thực hiện việc này. Bạn có thể giải thích thêm một chút về kịch bản?

+12

Testability sẽ là lý do chính của tôi, nhưng bất kỳ lý do gì để sử dụng DI cũng áp dụng tốt cho Cmdlets - tại sao chúng phải khác với phần còn lại của mã của tôi? –

3

Sử dụng PSCmdlet làm lớp cơ sở yêu cầu RunSpace để thực thi và chỉ cho phép bạn chỉ định lệnh để thực thi dưới dạng chuỗi. Xem this link để biết ví dụ.

Tôi đã chuyển về Cmdlet làm lớp cơ sở và sử dụng tính năng Tiêm thuộc tính để đặt phụ thuộc. Không phải là giải pháp sạch nhất, nhưng nó đã làm việc cho tôi. Những điều tốt đẹp về Cmdlet như một lớp cơ sở là bạn có thể gọi trực tiếp từ các đơn vị kiểm tra như thế này:

 var cmdlet = new MyCmdlet { 
          Dependency = myMockDependencyObject 
         }; 

     var result = cmdlet.Invoke().GetEnumerator(); 

     Assert.IsTrue(result.MoveNext()); 
+1

yeah tôi đã suy nghĩ về điều đó nhưng tôi đang cố gắng tránh việc sử dụng Cmdlet, tôi đang sử dụng trạng thái phiên hiện tại của Powershell. Vì vậy, đây không phải là một lựa chọn cho tôi :( –

2

Để Mở rộng vào Start-Tự động trả lời:

Về cơ bản, bạn sẽ có giao diện IView của bạn mà sẽ xác định các hoạt động cho PSCmdlet (WriteObject, WriteError, WriteProgress, và vv), bạn sẽ có việc thực hiện của bạn mà View sẽ là Commandlet thực tế.

Ngoài ra, bạn sẽ có Bộ điều khiển, là chức năng thực tế. Trên hàm khởi tạo, Bộ điều khiển nhận được một IProvider (Cái mà bạn muốn giả lập) và một IView. nhà cung cấp thực hiện cuộc gọi đến nhà cung cấp và ghi kết quả vào IView, nó sẽ phản ánh trên IView (Powershell Commandlet).

Trong quá trình khởi tạo Chế độ xem, bạn sẽ tạo Bộ điều khiển, tự đi qua (IView) và Nhà cung cấp, sau đó nó sẽ thực hiện thao tác với bộ điều khiển.

Với phương pháp này, Cmdlet của bạn là một lớp mỏng không thực hiện bất kỳ logic nghiệp vụ nào và mọi thứ đều nằm trên bộ điều khiển của bạn, đây là một thành phần có thể kiểm chứng.

0

Mặc dù bạn không thể sử dụng công cụ xây dựng cho việc này, bạn có thể sử dụng lệnh ghép ngắn. Có một cuộc gọi đầu tiên đến nó trên một tham số được thiết lập để khởi tạo, lưu trữ thông tin liên quan trong trạng thái phiên hiện tại và có các cuộc gọi tiếp theo kéo giá trị được lưu trữ trở lại từ trạng thái phiên.

Ở đây tôi đã sử dụng một chuỗi đơn, message để thể hiện giá trị được lưu trữ; nhưng rõ ràng bạn có thể có nhiều tham số/của bất kỳ loại nào bạn thích.

NB: C# bên dưới được gói trong PowerShell, vì vậy bạn có thể kiểm tra toàn bộ nội dung trực tiếp trong PS.

$cs = @' 
using System.Management.Automation; 

[Cmdlet(VerbsDiagnostic.Test, "Ploeh", DefaultParameterSetName = "None")] 
public class PloehCmdlet : PSCmdlet 
{ 
    const string InitialiseParameterSetName = "Initialise"; 
    const string MessageVariable = "Test_Ploeh_Message_39fbe50c_25fc_48b1_8348_d155cad99e93"; //since this is held as a variable in the session state, make sure the name will not clash with any existing variables 

    [Parameter(Mandatory=true, ParameterSetName = InitialiseParameterSetName)] 
    public string InitialiseMessage 
    { 
     set { SaveMessageToSessionState(value); } 
    } 

    protected override void ProcessRecord() 
    { 
     if (this.ParameterSetName != InitialiseParameterSetName) //do not run the cmdlet if we're just initialising it 
     { 
      this.WriteObject(GetMessageFromSessionState()); 
      base.ProcessRecord(); 
     } 
    } 

    void SaveMessageToSessionState(string message) 
    { 
     this.SessionState.PSVariable.Set(MessageVariable, message); 
    } 
    string GetMessageFromSessionState() 
    { 
     return (string)this.SessionState.PSVariable.GetValue(MessageVariable); 
    } 

} 
'@ 
#Trick courtesy of: http://community.idera.com/powershell/powertips/b/tips/posts/compiling-binary-cmdlets 
$DLLPath = Join-Path $env:temp ('CSharpPSCmdLet{0:yyyyMMddHHmmssffff}.dll' -f (Get-Date)) 
Add-Type -OutputAssembly $DLLPath -Language 'CSharp' -ReferencedAssemblies 'System.Management.Automation.dll' -TypeDefinition $cs 
Import-Module -Name $DLLPath -Force -Verbose 

#demo commands 

Test-Ploeh -InitialiseMessage 'this is a test' 
Test-Ploeh 
Test-Ploeh 

Test-Ploeh -InitialiseMessage 'change value' 
Test-Ploeh 
Test-Ploeh 

"NB: Beware, your value can be accessed/amended outside of your cmdlet: $Test_Ploeh_Message_39fbe50c_25fc_48b1_8348_d155cad99e93" 
$Test_Ploeh_Message_39fbe50c_25fc_48b1_8348_d155cad99e93 = "I've changed my mind" 
Test-Ploeh 

Kết quả ví dụ

VERBOSE: Loading module from path 'C:\Users\UserNa~1\AppData\Local\Temp\CSharpPSCmdLet201711132257130536.dll'. 
VERBOSE: Importing cmdlet 'Test-Ploeh'. 
this is a test 
this is a test 
change value 
change value 
NB: Beware, your value can be accessed/amended outside of your cmdlet: change value 
I've changed my mind 
Các vấn đề liên quan