代码之家  ›  专栏  ›  技术社区  ›  Mark Bertenshaw

从一个函数到另一个函数的内部管道化

  •  0
  • Mark Bertenshaw  · 技术社区  · 6 年前

    我有一个模块,它有以下两个功能,几乎相同:

    <#
        .SYNOPSIS
        Retrieves a VApp from VCloud.
    #>
    Function Get-VApp
    {
        [CmdletBinding()]
        [OutputType([System.Xml.XmlElement])]
        Param(
            [Parameter(Mandatory = $true)]
            [System.Xml.XmlElement] $Session,
            [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
            [string[]] $VAppName
        )
        Begin {
            [System.Xml.XmlElement] $queryList = $Session.GetQueryList();
            [System.Xml.XmlElement[]] $vAppRecords = $queryList.GetVAppsByRecords().VAppRecord;
        }
        Process {
            ForEach ($VAN in $VAppName)
            {
                $vAppRecords |
                Where-Object { $_.name -eq $VAN } |
                ForEach-Object { $_.Get(); }
            }
        }
        End
        {
            #
        }
    }
    

    <#
        .SYNOPSIS
        Retrieves a VAppRecord from VCloud.
    #>
    Function Get-VAppRecord
    {
        [CmdletBinding()]
        [OutputType([System.Xml.XmlElement])]
        Param(
            [Parameter(Mandatory = $true)]
            [System.Xml.XmlElement] $Session,
            [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
            [string[]] $VAppName
        )
        Begin {
            [System.Xml.XmlElement] $queryList = $Session.GetQueryList();
            [System.Xml.XmlElement[]] $vAppRecords = $queryList.GetVAppsByRecords().VAppRecord;
        }
        Process {
            ForEach ($VAN in $VAppName)
            {
                $vAppRecords |
                Where-Object { $_.name -eq $VAN } |
                ForEach-Object { $_; }
            }
        }
        End
        {
            #
        }
    }
    

    本质上,get-vapp与get-vapprecord类似,只是前者对返回的对象调用get()方法。这似乎是浪费。如果我不去管管道,那很容易:

    Function Get-VApp
    {
            [CmdletBinding()]
            [OutputType([System.Xml.XmlElement])]
            Param(
                [Parameter(Mandatory = $true)]
                [System.Xml.XmlElement] $Session,
                [Parameter(Mandatory = $true)]
                [string[]] $VAppName
            )
        Get-VAppRecord $Session $VAppName |
        ForEach-Object {
            $_.Get();
        }
    }
    

    但很明显管道把事情搞砸了。为了提高效率,我不会多次调用begin块中的代码,我希望找到一种不必成批处理记录就可以“很好地”处理管道的方法。

    1 回复  |  直到 6 年前
        1
  •  2
  •   Mark Bertenshaw    6 年前

    SteppablePipeline

    ProxyCommand.Create()

    Get-VAppRecord

    $GetVAppRecordCommand = Get-Command Get-VAppRecord
    $GetVAppRecordCommandMetadata = [System.Management.Automation.CommandMetadata]::new($GetVAppRecordCommand)
    
    # returns the body of the new proxy functions
    [System.Management.Automation.ProxyCommand]::Create($GetVAppRecordCommandMetadata)
    

    Get() process

    function Get-VApp {
        [CmdletBinding()]
        param(
            [Parameter(Mandatory=$true, Position=0)]
            [System.Xml.XmlElement]
            ${Session},
    
            [Parameter(Mandatory=$true, Position=1, ValueFromPipeline=$true)]
            [string[]]
            ${VAppName})
    
        begin
        {
            try {
                $outBuffer = $null
                if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
                {
                    $PSBoundParameters['OutBuffer'] = 1
                }
                $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Get-VAppRecord', [System.Management.Automation.CommandTypes]::Function)
                $scriptCmd = {& $wrappedCmd @PSBoundParameters }
                $steppablePipeline = $scriptCmd.GetSteppablePipeline()
                $steppablePipeline.Begin($MyInvocation.ExpectingInput) # Many examples use $PSCmdlet; however setting this ensures that $steppablePipeline.Process() returns the output of the inner function.
            } catch {
                throw
            }
        }
    
        process
        {
            try {
                $steppablePipeline.Process($_) |ForEach-Object {
                    # call Get() on the record
                    $_.Get()
                }
            } catch {
                throw
            }
        }
    
        end
        {
            try {
                $steppablePipeline.End()
            } catch {
                throw
            }
        }
    }