C# Can';无法从管道获取值以在Powershell模块中工作
我正在创建一个Powershell模块,它充当与RESTful API接口的包装器 为了简化脚本编写者的工作,我提供了此Cmdlet来“建立与API的连接”(基本上是创建Worker类对象并指定身份验证令牌): 我遇到的问题是,我希望其他cmdlet能够接受由管道中的Get-APIConnection生成的对象。下面是我如何在另一个Cmdlet中实现该功能的示例:C# Can';无法从管道获取值以在Powershell模块中工作,c#,powershell,pipeline,C#,Powershell,Pipeline,我正在创建一个Powershell模块,它充当与RESTful API接口的包装器 为了简化脚本编写者的工作,我提供了此Cmdlet来“建立与API的连接”(基本上是创建Worker类对象并指定身份验证令牌): 我遇到的问题是,我希望其他cmdlet能够接受由管道中的Get-APIConnection生成的对象。下面是我如何在另一个Cmdlet中实现该功能的示例: [Cmdlet(VerbsCommon.Get, @"SecurityGroups")] [OutputType
[Cmdlet(VerbsCommon.Get, @"SecurityGroups")]
[OutputType(typeof(string))]
public class GetSecurityGroups : Cmdlet
{
private APIWorker connection;
[Parameter(Position = 0,
Mandatory = true,
ValueFromPipeline = true)]
public APIWorker Connection
{
get { return connection; }
set
{
//Confirmation that set is actually being called
Console.WriteLine(@"Got here!");
connection = value;
}
}
[Parameter(Mandatory = true)]
public string Identity { get; set; }
private IEnumerable<string> Result;
protected override void BeginProcessing()
{
base.BeginProcessing();
BuildOutputObject();
}
protected override void ProcessRecord()
{
base.ProcessRecord();
WriteObject(Result);
}
private BuildOutputObject()
{
Result = Connection.GetUserGroups(Identity);
}
…如果我将$Connection作为命名参数传递,则Get SecurityGroups和会起作用:
>Get-SecurityGroups -Connection $API -Identity 'abcdefg'
Got here!
PowershellUsers
DotNetCOE
CompanyCarOwners
...
请注意,“Got here!”被发送到屏幕,表明set确实在“Connection”参数上被调用
…但是,尽管我在“Get SecurityGroups”中为“Connection”参数指定了ValueFromPipeline属性,但由于某些原因,我无法从管道传递$API:
$API | Get-SecurityGroups -Identity 'abcdefg'
Get-SecurityGroups : Object reference not set to an instance of an object.
At line:1 char:7
+ $API | Get-SecurityGroups -Identity 'abcdefg'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-SecurityGroups], NullReferenceException
+ FullyQualifiedErrorId : System.NullReferenceException,APIHelper.GetSecurityGroups
异常详细信息表明BuildOutputObject()中正在引发空引用异常:
$error[0]|选择*
psmessage详情:
异常:System.NullReferenceException:对象引用未设置为对象的实例。
在APIHelper.GetSecurityGroups.BuildOutputObject()上
在System.Management.Automation.Cmdlet.DoBeginProcessing()中
在System.Management.Automation.CommandProcessorBase.DoBegin()中
目标对象:
CategoryInfo:NotSpecified:(:)[Get SecurityGroups],NullReferenceException
FullyQualifiedErrorId:System.NullReferenceException,APIHelper.GetSecurityGroups
错误详细信息:
调用信息:System.Management.Automation.InvocationInfo
ScriptStackTrace:at,:第1行
PipelineIterationInfo:{}
我很感兴趣的是,如果连接是一个强制参数,并且没有对其调用set(因此没有“get here!”),那么我完全能够访问BuildOutputObject()。正确的行为不应该是提示我定义那个参数吗
任何帮助都将不胜感激 在PowerShell管道中,参数绑定在进程块中完成,而不是在开始块中完成。如果您正在构建PowerShell commandlet,我假定同样的规则也适用 看看这个例子,它类似于您的
GetSecurityGroups
function Test-Pipeline {
[CmdletBinding()]
param (
[Parameter(Mandatory,ValueFromPipeline)]
[string]$a
)
BEGIN {Write-Verbose "begin $a"}
PROCESS {Write-Verbose "process $a"}
END {Write-Verbose 'end'}
}
在不使用管道的情况下调用它,会得到预期的输出:
PS C:\> Test-Pipeline 'abc' -Verbose
VERBOSE: begin abc
VERBOSE: process abc
VERBOSE: end
但是,用管道参数调用它差别不大。你看到了吗
PS C:\> 'abc' | Test-Pipeline -Verbose
VERBOSE: begin
VERBOSE: process abc
VERBOSE: end
是的,在begin block中,$a没有值!在本例中,$a是简单类型,因此没有错误。在您的例子中,调用null
对象的方法,这样您就得到了错误
原因很简单。在本例中,它是可见的
PS C:\> 'abc','def' | Test-Pipeline -Verbose
VERBOSE: begin
VERBOSE: process abc
VERBOSE: process def
VERBOSE: end
管道执行甚至在管道中每个PowerShell函数的execution begin块处理第一个值之前就开始了
另外,如果你尝试以下方法,我想你也会得到意想不到的结果:
'[My Token]' | Get-APIConnection
简单地说,我的问题是需要将BuildOutputObject()方法移到ProcessRecord()中。执行BeginProcessing()时尚未读取管道。因此,在ProcessRecord()方法中设置输出对象的值
[Cmdlet(VerbsCommon.Get, @"SecurityGroups")]
[OutputType(typeof(string))]
public class GetSecurityGroups : Cmdlet
{
private APIWorker connection;
[Parameter(Position = 0,
Mandatory = true,
ValueFromPipeline = true)]
public APIWorker Connection
{
get { return connection; }
set
{
//Confirmation that set is actually being called
Console.WriteLine(@"Got here!");
connection = value;
}
}
[Parameter(Mandatory = true)]
public string Identity { get; set; }
private IEnumerable<string> Result;
protected override void BeginProcessing()
{
base.BeginProcessing();
}
protected override void ProcessRecord()
{
base.ProcessRecord();
BuildOutputObject();
WriteObject(Result);
}
private BuildOutputObject()
{
Result = Connection.GetUserGroups(Identity);
}
[Cmdlet(VerbsCommon.Get,@“SecurityGroups”)]
[输出类型(类型(字符串))]
公共类GetSecurityGroups:Cmdlet
{
私人联系;
[参数(位置=0,
强制=真,
ValueFromPipeline=true)]
公共工作者联系
{
获取{返回连接;}
设置
{
//确认集合实际上正在被调用
控制台。WriteLine(@“到了!”);
连接=值;
}
}
[参数(强制=真)]
公共字符串标识{get;set;}
私人的不可数结果;
受保护的覆盖void BeginProcessing()
{
base.BeginProcessing();
}
受保护的覆盖无效ProcessRecord()
{
base.ProcessRecord();
BuildOutputObject();
写入对象(结果);
}
私有BuildOutputObject()
{
结果=Connection.GetUserGroups(标识);
}
如果删除位置=0,
位,是否会遇到同样的问题?@TheMadTechnician是的,尝试了在结束括号前缺少)
[参数(位置=0,必需=true,ValueFromPipeline=true)]@cf谢谢,这是将代码改装为在SO上公开发布的一个输入错误。添加ValueFromPipelineByPropertyName=true
,这样一个对象只要有一个具有可接受名称的属性(在本例中为连接
)就可以通过管道传输我还添加了一个稍微复杂一点的示例。所以,长话短说,如果我在ProcessRecord中调用BuildOutputObject而不是BeginProcess,我应该很好,对吧?一般来说,是的。任何依赖于管道中的值的操作都不应该在begin块中完成。但是,正如最后一个示例,请注意,ProcessRecord
可以是c我打了好几次电话。
PS C:\> 'abc','def' | Test-Pipeline -Verbose
VERBOSE: begin
VERBOSE: process abc
VERBOSE: process def
VERBOSE: end
'[My Token]' | Get-APIConnection
[Cmdlet(VerbsCommon.Get, @"SecurityGroups")]
[OutputType(typeof(string))]
public class GetSecurityGroups : Cmdlet
{
private APIWorker connection;
[Parameter(Position = 0,
Mandatory = true,
ValueFromPipeline = true)]
public APIWorker Connection
{
get { return connection; }
set
{
//Confirmation that set is actually being called
Console.WriteLine(@"Got here!");
connection = value;
}
}
[Parameter(Mandatory = true)]
public string Identity { get; set; }
private IEnumerable<string> Result;
protected override void BeginProcessing()
{
base.BeginProcessing();
}
protected override void ProcessRecord()
{
base.ProcessRecord();
BuildOutputObject();
WriteObject(Result);
}
private BuildOutputObject()
{
Result = Connection.GetUserGroups(Identity);
}