Подтвердить что ты не робот

Инъекция зависимостей с PowerShell

Можно ли использовать Injection Dependency (DI) с Windows 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");
    }
}

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

        var cmdlet = new MyCmdlet {
                             Dependency = myMockDependencyObject
                         };

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

        Assert.IsTrue(result.MoveNext());

Ответ 3

Чтобы развернуть ответ "Пуск-Автоматизация":

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

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

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

При таком подходе ваш Cmdlet - это тонкий слой, который не выполняет никакой бизнес-логики, и все находится на вашем контроллере, который является тестируемым компонентом.

Ответ 4

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

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

NB: ниже С# завернут в 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