用于缓冲管道输入到PowerShell cmdlet的设计模式

我偶尔会遇到支持Cmdlet的管道输入是有意义的情况,但我希望做的操作(例如数据库访问)对批量处理合理数量的对象是有意义的.

实现此目的的典型方法似乎如下:

function BufferExample {
<#
.SYNOPSIS
Example of filling and using an intermediate buffer.
#>
[CmdletBinding()]
param(
    [Parameter(ValueFromPipeline)]
    $InputObject
)

BEGIN {
    $Buffer = New-Object System.Collections.ArrayList(10)
    function _PROCESS {
        # Do something with a batch of items here.
        Write-Output "Element 1 of the batch is $($Buffer[1])"
        # This could be a high latency operation such as a DB call where we 
        # retrieve a set of rows by primary key rather than each one individually.

        # Then empty the buffer.
        $Buffer.Clear()
    }
}

PROCESS {
    # Accumulate the buffer or process it if the buffer is full.
    if ($Buffer.Count -ne $Buffer.Capacity) {    
        [void]$Buffer.Add($InputObject)        
    } else {
        _PROCESS 
    }
}

END {
     # The buffer may be partially filled, so let's do something with the remainder.
    _PROCESS
}
}

有没有更少的“样板”方法来做到这一点?

一种方法可能是在这里编写我称之为“_PROCESS”的函数来接受数组参数但不接受管道输入,然后为暴露给用户的cmdlet创建一个代理函数,用于缓冲输入并传递缓冲区如描述的那样
Proxy commands.

或者,我可以在我希望编写的cmdlet主体中点源动态代码以支持此功能,但这似乎容易出错并且可能难以调试/理解.

最佳答案 管道的性质给你(你很容易)按照你想要的方式做了一些约束,主要是因为Process块被设计用来接收(和处理)一个对象.

如果你想缓冲所有对象,这很简单;您只需收集Process块中的数组或其他集合中的所有对象,然后在End块中完成所有工作;类似于Sort-Object这样的cmdlet处理它的方式.

在为了底层资源而缓冲的情况下,比如基于Web的API,或者您的数据库访问示例,我认为您采用的方法需要针对具体情况.实现它的可能性不大.

其中一种方法是将操作分成2个(可能更多?)函数.

例如,我编写了一些函数来向Graphite发送指标.我在Format-Graphite和Out-Graphite之间拆分了这些功能;前者根据参数和管道生成格式正确的度量字符串,而后者将字符串发送到Graphite收集器.它允许客户端代码在获取和生成数据方面更加通用,因为它可以管道传输到Format-Graphite,或者单独调用它而不必担心网络部分效率低下.客户端代码不必处理手动收集自己的数据只是为了避免这种情况.不是没有代码演示的最佳示例,但我现在无法发布该代码.

对于单个操作的“昂贵”部分是初始化和拆除代码的事情的另一种方法是在Begin和End中执行该操作,然后正常使用Process.

例如,通过在Begin中建立的单个连接进行数十次数据库调用可能不会那么糟糕,甚至可能比建立一个大的SQL字符串并一次性发送它更可取.

最后,我认为您可能会更好地查看每个单独的用例并确定满足您需求的最佳方法,平衡性能/效率以及调用代码的简便性/直观性.

如果您有特定用例并发布有关该问题的问题,我想阅读它;给我一个链接.

点赞