2009-12-22 76 views
23

使用Windows PowerShell进行依赖注入(DI)有可能吗?使用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可以注册和使用,但没有默认的构造函数,这是根本无法获得。

我知道我可以使用服务定位器来检索我的依赖关系,但我认为这是一种反模式,所以不想这样做。

我本来希望PowerShell的API有着某种“厂”钩类似WCF的ServiceHostFactory,但如果有,我无法找到它。

+0

我想知道为好。 – 2009-12-22 18:07:30

+1

我很难为此提供一个很好的用例。 – yfeldblum 2009-12-23 20:53:25

+0

我正在使用服务定位器。 :) – 2010-09-12 21:18:53

回答

10

命令类的一个实例从一个空的构造每个小命令在PowerShell中使用时创建的。您无法控制PowerShell将选择哪个构造函数,因此您无法直接执行您提出的建议(而且我真的很难想象您为什么要这么做)。所以这个问题的简单答案是NO。为了实现类似的效果,可以创建一个看起来像cmdlet(具有BeginProcessing/EndProcessing/ProcessRecord/StopProcessing)的接口,并使用该接口来填充实际代码中的一小堆精简包装的cmdlet。恕我直言,这将是一个过于复杂的方法。

我真的不明白你为什么要这样做。你能解释一下这个场景吗?

+12

可测试性将是我的主要原因,但任何使用DI的理由同样适用于Cmdlet - 为什么它们应该与我的其他代码不同? – 2009-12-23 21:17:14

3

使用PSCmdlet作为基类需要一个运行空间来执行,只允许您指定执行作为字符串命令。示例见this link

我切换回Cmdlet作为基类,并使用Property注入来设置依赖关系。不是最干净的解决方案,但它为我工作。有关cmdlet上的好处作为基类是,你可以从这样的单元测试直接调用它:

 var cmdlet = new MyCmdlet { 
          Dependency = myMockDependencyObject 
         }; 

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

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

是的,我在想,但我试图避免使用Cmdlet,我使用Powershell的当前会话状态。所以这对我来说不是一个很好的选择:( – 2012-12-11 23:08:20

2

扩大在启动自动化答案:

从本质上讲,你将有你的iView界面将定义PSCmdlet的操作(WriteObject,WriteError,WriteProgress等),您将实现该视图,这将是实际的Commandlet。

此外,您将有一个控制器,这是实际功能。在构造函数中,Controller接收一个IProvider(这是你想要模拟的)和一个IView。提供程序执行对提供程序的调用,并将结果写入IView,这将反映在IView(Powershell Commandlet)上。

期间,您将创建一个控制器,以及本身(IVIEW)和提供商通过,那么它将对控制器进行操作视图的初始化。

用这种方法您的小命令不执行任何业务逻辑的薄层,和一切是你的控制器,它是是可测试的部件上。

0

虽然您不能使用此构造函数注入,但您可以使用cmdlet本身。在一个参数集上进行第一次调用以初始化,将相关信息存储在当前会话状态中,并且随后的调用将存储的值从会话状态中拉回。

这里我用了一个字符串message来表示存储的值;但显然你可以有任意多的参数/你喜欢的任何类型。

注意:以下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