是否可以在Windows PowerShell中使用依赖注入(DI)?
我的初步实验表明事实并非如此。如果我尝试在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的'Factory'钩子,但是如果有的话,我找不到它。
答案 0 :(得分:10)
每次在PowerShell中使用cmdlet时,都会从空构造函数创建cmdlet类的实例。您无法控制PowerShell将选择哪个构造函数,因此您无法以直接的方式执行您提出的建议(我真的很难想象您为什么要这样做)。所以这个问题的简单答案是否定的。
为了实现类似的效果,您可以创建一个看起来像cmdlet的接口(具有BeginProcessing / EndProcessing / ProcessRecord / StopProcessing)并用于填充一堆cmtrats,这些cmdlet是真实代码上的瘦包装器。恕我直言,这将是一个过于复杂的方法。
我真的不明白你为什么要这样做。你能解释一下这个场景吗?
答案 1 :(得分:3)
使用PSCmdlet作为基类需要执行RunSpace,并且只允许您指定要作为字符串执行的命令。有关示例,请参阅this link。
我切换回Cmdlet作为基类,并使用Property注入来设置依赖项。不是最干净的解决方案,但它对我有用。关于Cmdlet作为基类的好处是你可以直接从单元测试中调用它,如下所示:
var cmdlet = new MyCmdlet {
Dependency = myMockDependencyObject
};
var result = cmdlet.Invoke().GetEnumerator();
Assert.IsTrue(result.MoveNext());
答案 2 :(得分:2)
展开 - 自动回答:
基本上你将有你的IView接口来定义PSCmdlet(WriteObject,WriteError,WriteProgress等)的操作,你将实现那个View实际的Commandlet。
您还将拥有一个Controller,它是Actual functionallity。在构造函数上,Controller接收一个IProvider(你想要模拟的那个)和一个IView。提供者执行对提供者的调用并将结果写入IView,这将反映在IView(Powershell Commandlet)上。
在View初始化期间,您将创建一个Controller,传递它自己(IView)和一个Provider,然后它将对控制器执行操作。
使用这种方法,您的Cmdlet是一个不执行任何业务逻辑的薄层,而且一切都在您的控制器上,这是一个可测试的组件。
答案 3 :(得分:0)
虽然您不能使用构造函数注入,但您可以使用cmdlet本身。在一个参数集上首次调用它以初始化,将相关信息存储在当前会话状态,然后让后续调用将存储的值从会话状态拉回。
这里我使用了一个字符串> Enter the list containing the data: Traceback (most recent call last):
> File "make_histo.py", line 9, in <module>
> df.plot(kind="bar") File "/usr/local/lib/python2.7/dist-packages/pandas/plotting/_core.py",
> line 2627, in __call__
> sort_columns=sort_columns, **kwds) File "/usr/local/lib/python2.7/dist-packages/pandas/plotting/_core.py",
> line 1869, in plot_frame
> **kwds) File "/usr/local/lib/python2.7/dist-packages/pandas/plotting/_core.py",
> line 1694, in _plot
> plot_obj.generate() File "/usr/local/lib/python2.7/dist-packages/pandas/plotting/_core.py",
> line 243, in generate
> self._compute_plot_data() File "/usr/local/lib/python2.7/dist-packages/pandas/plotting/_core.py",
> line 352, in _compute_plot_data
> 'plot'.format(numeric_data.__class__.__name__))
TypeError: Empty 'DataFrame': no numeric data to plot
来表示存储的值;但显然你可以拥有任何类型的参数/你喜欢的任何类型。
注意:下面的C#包含在PowerShell中,因此您可以直接在PS中测试整个内容。
message
$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