Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Is this a good "pattern" for processing collection-based parameters which belong to parameter sets of a function?

I’ve been writing advanced functions for many years now and have even written quite a few modules at this point. But there’s one question for which I have never really been able to find an answer.

Let’s look at a Cmdlet that Microsoft provides in the MSMQ module, as an example, and "re-implement" it as an advanced PowerShell function: Send-MsmqQueue. But this function will be a bit different than the one provided by the MSMQ module in that not only will it accept multiple MSMQ queues for the $InputObject parameter, but also multiple MSMQ queue names for the $Name parameter, where these two parameters belong to different parameter sets. (The Cmdlet version of this function normally only accepts a single string value for the $Name parameter.) I won’t be showing a complete re-implementation, just enough to illustrate what I, at times, find myself doing when this situation arises. (NOTE: one other slight difference is that I will be using the classes from System.Messaging namespace instead of the PowerShell-provided ones in Microsoft.Msmq.PowerShell.Commands namespace. So assume that implicitly, somewhere, Add-Type -AssemblyName System.Messaging has been executed.)

function Send-MsmqQueue {
    [CmdletBinding(DefaultParameterSetName = 'Name')]
    [OutputType([Messaging.Message])]
    Param (
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ParameterSetName = 'InputObject')
        ]
        [Messaging.MessageQueue[]] $InputObject,

        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ParameterSetName = 'Name')
        ]
        [string[]] $Name,

        # Below is the original parameter name, not mine ;)
        [Messaging.Message] $MessageObject

        # All other normal Send-MsmqQueue parameters elided as they are not
        # needed to illustrate the premise of my question.
    )

    Process {
        # When I have parameters defined as above, the first thing I do in my
        # Process block is "homogenize" the data so I don't have to implement
        # two foreach loops or do the branching on each foreach loop iteration
        # which can obscure the main logic that is being executed, i.e., I get
        # this done all "up-front".
        #
        # One aspect of my question is, from purely a PowerShell perspective,
        # is this hurting performance in any meaningful way? (I know that when it
        # comes to specific implementation details, there are INFINITE ways to
        # write non-performant code, so from purely a PowerShell perspective,
        # as far as the language design/inner-workings, is this hurting
        # performance?
        #
        # NOTE: I don't normally need the wrapping "force this thing to be an
        # array" construct (,<array_items>), BUT, in this case, the C#
        # System.Messaging.MessageQueue class implements IEnumerable,
        # which PowerShell (unhelpfully) iterates over automatically, and results
        # in the messages in the queues being iterated over instead of the queues
        # themselves, so this is an implementation detail specific to this
        # particular function.
        $Queues = (,@(
            if ($PSCmdlet.ParameterSetName -ieq 'Name') {
                # Handle when the parameter is NOT passed by the pipeline...
                foreach ($n in $Name) { [Messaging.MessageQueue]::new($n) }
            } else {
                $InputObject
            }
        ))

        # I like using 'foreach (...) { ... }' instead of ForEach-Object because
        # oftentimes, I will need to break or continue based on implementation
        # details, and using ForEach-Object in combination with break/continue
        # causes the pipeline to prematurely exit.
        foreach ($q in $Queues) {
            $q.Send($MessageObject)
            # Normally, I wouldn't return this, especially since it wasn't
            # modified, but this is a re-implementation of MSFT's Send-MsmqQueue,
            # and it returns the sent message.
            $MessageObject
        }
    }
}

As I stated in the introduction to this question, I have written many functions which take varying collection-based parameters belonging to different parameter sets which can be piped into the function, and this is the pattern that I use. I’m hoping someone can either confirm that this is OK from a PowerShell language/style perspective and/or help me understand why I should not do this and what I ought to consider instead.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Thank you!

>Solution :

This pattern ("homogenizing" the input entities based on chosen parameter set) is perfectly valid, and constitutes – in my personal opinion at least – good parameter design.

That being said, you might want to use Write-Output -NoEnumerate to avoid the clunky ,@(...) unwrapped-wrapped-array unpacking trick:

if ($PSCmdlet.ParameterSetName -ieq 'Name') {
    # Handle when the parameter is NOT passed by the pipeline...
    $Queues = foreach ($n in $Name) {
        $queue = [Messaging.MessageQueue]::new($n)
        Write-Output $queue -NoEnumerate
    }
}
else {
    # Input is already [MessageQueue[]], avoid pipeline boundaries entirely
    $Queues = $InputObject 
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading