2009-12-22 4 views
23

Можно ли использовать инъекцию зависимостей (DI) с помощью Windows PowerShell?Инъекция зависимостей с помощью PowerShell

Мои интуитивные эксперименты предполагают, что это не так. Если я попытаюсь использовать Инъекция конструктора в CmdLet, он даже не зарегистрируется. Другими словами, это не представляется возможным:

[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"); 
    } 
} 

Если добавить конструктор по умолчанию, Командлет может быть зарегистрирован и используется, но без конструктора по умолчанию, это просто не доступно.

Я знаю, что я мог бы использовать Service Locator, чтобы получить мои зависимости, но я считаю, что анти-шаблон так не хочет идти таким образом.

Я бы надеялся, что у API PowerShell был какой-то крючок «Factory», похожий на WCF ServiceHostFactory, но если есть, я не могу его найти.

+0

мне очень интересно, как хорошо. –

+1

У меня тяжелое время подходит для хорошего использования. – yfeldblum

+0

Я использую локатор сервисов. :) –

ответ

10

Экземпляр класса cmdlet создается из пустого конструктора каждый раз, когда командлет используется в PowerShell. У вас нет контроля над тем, какой конструктор PowerShell выберет, поэтому вы не можете делать то, что вы предлагаете простым способом (и мне действительно трудно представить, почему вы захотите). Таким образом, простой ответ на этот вопрос НЕТ.

Чтобы получить аналогичный аффект, вы можете создать интерфейс, который выглядит как командлет (имеет BeginProcessing/EndProcessing/ProcessRecord/StopProcessing) и использовать для заполнения группы командлетов, которые являются тонкими оболочками поверх реального кода. ИМХО это было бы сложным подходом.

Я действительно не понимаю, почему вы пытаетесь это сделать. Не могли бы вы объяснить немного больше об этом сценарии?

+12

Испытание было бы моей главной причиной, но любая причина использовать DI одинаково хорошо подходит для командлетов - почему они должны отличаться от остальных моих кодов? –

3

Использование PSCmdlet в качестве базового класса требует выполнения RunSpace и позволяет указать команду для выполнения в виде строки. См. Пример this link.

Я переключился на Cmdlet в качестве базового класса и использовал инъекцию свойств для установки зависимостей. Не самое чистое решение, но это сработало для меня. Хорошая вещь о CMDlet в качестве базового класса является то, что вы можете вызвать его непосредственно из модульного тестирования, как это:

 var cmdlet = new MyCmdlet { 
          Dependency = myMockDependencyObject 
         }; 

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

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

Да, я думал об этом, но я пытаюсь избежать использования Cmdlet, я использую текущее состояние сеанса Powershell. Поэтому для меня это не очень подходит :( –

2

Для расширения Start-Автоматическое создание ответа:

По сути вы будете иметь свой интерфейс IView, что будет определять операции для PSCmdlet (WriteObject, WriteError, WriteProgress и т. д.), у вас будет ваша реализация этого представления, которая будет фактическим Commandlet.

Также у вас будет контроллер, который является фактическим функциональным назначением. На конструкторе Контроллер получает IProvider (который является тем, который вы хотите высмеять) и IView. провайдер выполняет вызов провайдера и записывает результаты IView, которые будут отражены в IView (CommandShell Powershell).

Во время инициализации представления вы создадите Контроллер, пройдите по нему (IView) и Провайдером, а затем выполните операцию против контроллера.

С помощью этого подхода ваша Командная строка представляет собой тонкий слой, который не выполняет никакой бизнес-логики, и все на вашем контроллере, который является тестируемым компонентом.

0

Хотя вы не можете использовать инъекцию конструктора для этого, вы можете использовать сам командлет. Сначала вызовите его на один набор параметров для инициализации, сохраните соответствующую информацию в текущем состоянии сеанса и последующие вызовы вытащите сохраненное значение из состояния сеанса.

Здесь я использовал одну строку, message для представления сохраненного значения; но, очевидно, у вас может быть столько параметров/любых типов, которые вам нравятся.

NB: приведенный ниже C# завернут в PowerShell, так что вы можете протестировать все это прямо в 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 

Результат

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 
Смежные вопросы