简体   繁体   English

带名称属性的Powershell New-WebBinding管道问题

[英]Powershell New-WebBinding Pipeline Issue with Name Property

I'm encountering an issue with New-WebBinding when piping in an object. 在管道对象时遇到New-WebBinding的问题。 I have an object that defines 5 properties: Name, Protocol, Port, IPAddress and HostHeader (all 5 are supported in the New-WebBinding cmdlet as Accept Pipeline input: ValueByPropertyName). 我有一个对象,它定义了5个属性:Name,Protocol,Port,IPAddress和HostHeader(New-WebBinding cmdlet支持所有5个作为Accept Pipeline输入:ValueByPropertyName)。 However, when you pipe in this object, it still requests a Name: to be submitted. 但是,当您在此对象中进行管道传输时,它仍然会请求提交名称:。 Here is a quick test function if you'd like to duplicate the issue. 如果您想复制该问题,这是一个快速测试功能。 If you hit enter at the prompt, it successfully processes the objects, adding the bindings. 如果在提示符处按Enter键,则会成功处理对象,添加绑定。 But the prompt itself breaks it as a non-interactive script. 但提示本身将其作为非交互式脚本分解。

I've tested this with both PS v3 and PS v4. 我用PS v3和PS v4进行了测试。

I'm pretty sure I'm doing this all correctly but wanted to make sure there wasn't something I might be overlooking. 我很确定我这样做是正确的,但我想确保没有我可能会忽略的东西。 For now I'm just iterating through my object collection in a foreach loop which does not have this issue but would like to see if this is a bug I should report. 现在我只是在foreach循环中迭代我的对象集合,它没有这个问题,但是想知道这是否是我应该报告的错误。

function Test-WebBinding{
   [CmdletBinding()]
   Param()

   $testBindingCol = @()

   $testBinding1 = New-Object System.Object
   $testBinding1 | Add-Member -MemberType NoteProperty -Name Name -Value 'Default Web Site'
   $testBinding1 | Add-Member -MemberType NoteProperty -Name Protocol -Value 'https'
   $testBinding1 | Add-Member -MemberType NoteProperty -Name Port -Value '4000'
   $testBinding1 | Add-Member -MemberType NoteProperty -Name IPAddress -Value '*'
   $testBinding1 | Add-Member -MemberType NoteProperty -Name HostHeader -Value 'Test4000'
   $testBindingCol += $testBinding1

   $testBinding2 = New-Object System.Object
   $testBinding2 | Add-Member -MemberType NoteProperty -Name Name -Value 'Default Web Site'
   $testBinding2 | Add-Member -MemberType NoteProperty -Name Protocol -Value 'http'
   $testBinding2 | Add-Member -MemberType NoteProperty -Name Port -Value '4001'
   $testBinding2 | Add-Member -MemberType NoteProperty -Name IPAddress -Value '*'
   $testBinding2 | Add-Member -MemberType NoteProperty -Name HostHeader -Value 'Test4001'
   $testBindingCol += $testBinding2

   $testBindingCol | New-WebBinding
}

PetSerAl is onto the right idea with his comment above: 通过以上评论,PetSerA是正确的想法:

One workaround would be to change current location to some site ( cd IIS:\\Sites\\SomeSite ), it does not really matter to which 一种解决方法是将当前位置更改为某个站点( cd IIS:\\Sites\\SomeSite ),这对哪个不重要

That does actually work, but why doesn't it work from the normal file system prompt? 这确实有效,但为什么它不能正常的文件系统提示?

To discover why New-WebBinding behaves this way I loaded the Microsoft.IIS.PowerShell.Provider assembly containing this and other WebAdministration cmdlets into dotPeek. 为了解释为什么New-WebBinding以这种方式运行,我将包含this和其他WebAdministration cmdlet的Microsoft.IIS.PowerShell.Provider程序集加载到dotPeek中。 The assembly lives in the GAC so you tell dotPeek to "Open from GAC". 该程序集存在于GAC中,因此您可以告诉dotPeek“从GAC打开”。

When loaded, the class we're interested in is called NewWebBindingCommand . 加载时,我们感兴趣的类称为NewWebBindingCommand

Upon a cursory inspection we can see that all of the parameter properties are decorated with the [Parameter(ValueFromPipelineByPropertyName = true)] attribute so that's a good start, piping an array of objects with matching property names ought to work: 粗略检查后,我们可以看到所有参数属性都使用[Parameter(ValueFromPipelineByPropertyName = true)]属性进行修饰,因此这是一个良好的开端,管道具有匹配属性名称的对象数组应该起作用:

在此输入图像描述

NewWebBindingCommand ultimately inherits from System.Management.Automation.Cmdlet and in this instance is overriding the BeginProcessing method. NewWebBindingCommand最终继承自System.Management.Automation.Cmdlet并在此实例中重写BeginProcessing方法。 If overridden, BeginProcessing is called by PowerShell and "Provides a one-time, preprocessing functionality for the cmdlet." 如果被覆盖,PowerShell将调用BeginProcessing为“cmdlet提供一次性预处理功能”。

It is important to understand that a cmdlet's BeginProcessing override is called before any pipeline fed named parameters are processed and bound to the cmdlet's properties (see: Cmdlet Processing Lifecycle (MSDN) ) . 重要的是要了解在处理任何传递命名参数的管道并绑定到cmdlet的属性之前调用cmdlet的BeginProcessing覆盖(请参阅: Cmdlet处理生命周期(MSDN) )。

Our New-WebBinding cmdlet's implementation of BeginProcessing looks like: 我们的New-WebBinding cmdlet的BeginProcessing实现如下所示:

protected override void BeginProcessing()
{
  base.BeginProcessing();
  if (!string.IsNullOrEmpty(this.siteName))
    return;
  this.siteName = this.GetSiteName("Name");
}

this.siteName is the private member value for the Name property which would be bound to the -Name parameter. this.siteNameName属性的私有成员值,它将绑定到-Name参数。 When we arrive at the if(...) statement above `this.siteName isn't yet bound (it's null) and so falls through to: 当我们到达上面的if(...)语句时,`this.siteName尚未绑定(它为空),因此落到:

this.siteName = this.GetSiteName("Name");

The call to GetSiteName() calls up to the cmdlet's immediate base class HelperCommand which provides a number of "helper" methods that are useful to many different WebAdministration cmdlets. GetSiteName()调用会调用cmdlet的直接基类HelperCommand ,它提供了许多对许多不同WebAdministration cmdlet有用的“帮助程序”方法。

HelperCommand.GetSiteName(string prompt) looks like this: HelperCommand.GetSiteName(string prompt)如下所示:

protected string GetSiteName(string prompt)
{
  PathInfo pathInfo = this.SessionState.PSVariable.Get("PWD").Value as PathInfo;
  if (pathInfo != null && pathInfo.Provider.Name.Equals("WebAdministration", StringComparison.OrdinalIgnoreCase))
  {
    string[] strArray = pathInfo.Path.Split('\\');
    if (strArray.Length == 3 && strArray[1].Equals("sites", StringComparison.OrdinalIgnoreCase))
      return strArray[2];
  }
  if (!string.IsNullOrEmpty(prompt))
    return this.PromptForParameter<string>(prompt);
  return (string) null;
}

For the purpose of learning about this issue I created my own PowerShell cmdlet (called the Kevulator , sorry) and pulled in the New-WebBinding cmdlet's BeginProcessing() code and the code from New-WebBinding 's base class helper method GetSiteName() . 为了解这个问题,我创建了自己的PowerShell cmdlet(称为Kevulator ,对不起),并New-WebBindingNew-WebBinding cmdlet的BeginProcessing()代码和New-WebBinding的基类帮助方法GetSiteName()

Here's a screenshot of breaking inside GetSiteName in VS2015 when attached to a PowerShell session piping in your bindings to New-Kevulator : 这是在绑定到New-Kevulator时连接到PowerShell会话管道时,在GetSiteName中打破GetSiteName内部的屏幕截图:

在此输入图像描述

The top arrow demonstrates that our Name property backed by siteName hasn't yet been bound and is still null (which as a mentioned above causes GetSiteName to be executed). 顶部箭头表明我们的siteName支持的Name属性尚未绑定,仍然为null (如上所述,导致GetSiteName被执行)。 We've also just stepped past a break point on this line: 我们刚刚走过这条线的断点:

PathInfo pathInfo = 
        this.SessionState.PSVariable.Get("PWD").Value as PathInfo;

...which determines what kind of path provider we're sitting at. ...它决定了我们所处的路径提供者。 In this case we're on a normal filesystem C:\\> prompt so the provider name will be FileSystem . 在这种情况下,我们处于正常的文件系统C:\\>提示符下,因此提供程序名称将是FileSystem I've highlighted this with the second arrow. 我用第二个箭头强调了这一点。

If we Import-Module WebAdministration and CD IIS: then re-run and break again you can see that the path provider has changed to WebAdministration which is responsible for handling life inside IIS:> and beyond: 如果我们使用Import-Module WebAdministrationCD IIS:然后重新运行并再次中断,您可以看到路径提供程序已更改为WebAdministration ,它负责处理IIS:>内部的生活IIS:>及更高:

在此输入图像描述

If the pathInfo name is not equal to WebAdministration , ie we're not at an IIS: prompt, then the code falls through and will prompt for the Name parameter at the command line, just as you've experienced. 如果pathInfo名称不等于WebAdministration ,即我们不在IIS:提示符下,则代码将通过,并将在命令行中提示Name参数,就像您所经历的那样。

If the pathInfo value is WebAdministration then one of two things will happen: 如果pathInfo WebAdministration ,则会发生以下两种情况之一:

If the path is IIS: or IIS:\\Sites the code falls through and issues a prompt for the Name parameter. 如果路径是IIS:IIS:\\Sites则代码将通过并发出Name参数的提示。

If the path is IIS:\\Sites\\SomeSiteName then SomeSiteName is returned and effectively becomes the -Name parameter value and we bail out from the GetSiteName() function back to BeginProcessing . 如果路径是IIS:\\Sites\\SomeSiteName则返回SomeSiteName并有效地成为-Name参数值,我们从GetSiteName()函数退回到BeginProcessing

That last behaviour is useful because if your current path is say IIS:\\Sites\\MySite then you can pipe in an array of bindings for that site but without specifying the Name parameter. 最后一个行为很有用,因为如果您当前的路径是IIS:\\Sites\\MySite那么您可以管道该站点的绑定数组,但不指定Name参数。 Alternatively if you do provide a Name property in your piped array of objects then these will override the default site name picked up from your IIS:\\Sites\\MySite context. 或者,如果您在管道对象数组中提供了Name属性,则这些属性将覆盖从IIS:\\Sites\\MySite上下文中获取的默认站点名称。

So, getting back to our code, once BeginProcessing has run its course our pipeline named parameters are now actually bound and then the ProcessRecord method is called, which is the meat and potatoes work that New-WebBinding has to perform. 所以,回到我们的代码,一旦BeginProcessing运行它的路线,我们的管道命名参数现在实际上被绑定,然后调用ProcessRecord方法,这是New-WebBinding必须执行的肉和土豆工作。

TLDR: TLDR:

New-WebBinding won't bind pipelined parameters unless you change your current working directory to an IIS website, eg, cd iis:\\Sites\\MySite . 除非将当前工作目录更改为IIS网站,否则New-WebBinding不会绑定管道化参数,例如cd iis:\\Sites\\MySite

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM