Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set default value for dynamic param?

Tags:

powershell

I tried to add System.ComponentModel.DefaultValueAttribute to AttributeCollection of RuntimeDefinedParameter, but it does't work..

like image 208
Jack128 Avatar asked Jun 13 '12 09:06

Jack128


3 Answers

Chrissy's example is probably the correct way to do it, but I was unable to retrieve the default value. The parameter does not exist in $PSBoundParameters when default value is specified.

The "workaround" we applied was to bind $PSBoundParameter["Background"] to the value we want as default. $PSBoundParameters["Background"] = "Transparent"

Extending Chrissy's example:

DynamicParam  {
    [void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
    $colorlist = [System.Enum]::GetNames([System.Drawing.KnownColor])

    $attributes = New-Object System.Management.Automation.ParameterAttribute
    $attributes.ParameterSetName = "__AllParameterSets"
    $attributes.Mandatory = $false

    # Background color
    $validationset = New-Object -Type System.Management.Automation.ValidateSetAttribute -ArgumentList $colorlist
    $collection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
    $collection.Add($attributes)
    $collection.Add($validationset)
    $background = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("Background", [String], $collection)
    $PSBoundParameters["Background"] = "Transparent"

    $newparams = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
    $newparams.Add("Background", $background)

    return $newparams
}
like image 68
Tomas Widsell Avatar answered Sep 29 '22 19:09

Tomas Widsell


As Bartek suggested, the Value property can be used as seen in the code below

DynamicParam  {
    [void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
    $colorlist = [System.Enum]::GetNames([System.Drawing.KnownColor])

    $attributes = New-Object System.Management.Automation.ParameterAttribute
    $attributes.ParameterSetName = "__AllParameterSets"
    $attributes.Mandatory = $false

    # Background color
    $validationset = New-Object -Type System.Management.Automation.ValidateSetAttribute -ArgumentList $colorlist
    $collection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
    $collection.Add($attributes)
    $collection.Add($validationset)
    $background = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("Background", [String], $collection)
    $background.Value = "Transparent"

    $newparams = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
    $newparams.Add("Background", $background)

return $newparams
}

The important line here is $background.Value = "Transparent" where $background is a RunTimeDefinedParameter.

For those that are curious. I initial tried to use it as an attribute, but there is no .Value available within ParameterAttributes.

like image 38
Chrissy LeMaire Avatar answered Sep 29 '22 21:09

Chrissy LeMaire


UPDATE:
I found an error in my PowerShell function, Test-DynamicParameter, which led to an erroneous conclusion. Below, in the PowerShell function, I will comment out the erroneous line. Also, the very last example output has changed. Again, I will annotate the changes.

OK, so everyone who has answered is correct in how to set the "default" value of a dynamic parameter. (Spoiler alert / tl;dr: there's no such thing.) Now, let me preface my answer by saying that the function I'm about to demonstrate was executed via PowerShell 5.1. Below is what I found in my testing.

First, the function I'm using to test dynamic parameters and default values:


function Test-DynamicParameter {
    [CmdletBinding()]
    Param (
        [switch]$UseDynamicParameterDefault2000
    )

    dynamicparam {
        # $RequestTimeout parameter
        $attributeCollection = New-Object Collections.ObjectModel.Collection[Attribute]
        $attributeCollection.Add((New-Object Management.Automation.ParameterAttribute -Property @{ ParameterSetName = "__AllParameterSets" }))
        $attributeCollection.Add((New-Object Management.Automation.ValidateScriptAttribute { $_ -ge 0 }))

        $RequestTimeoutParameter = New-Object Management.Automation.RuntimeDefinedParameter RequestTimeout, int, $attributeCollection
        # This line uses an incorrect name for the dynamic parameter, which caused
        # incorrect results in my original post. The corrected line
        # appears below this commented out, incorrect line.
        #$RequestTimeoutParameter.Value = $(if ($PSBoundParameters.UseDynamicParameterDefault1) { 2000 } else { 120000 })
        $RequestTimeoutParameter.Value = $(if ($PSBoundParameters.UseDynamicParameterDefault2000) { 2000 } else { 120000 })

        $dynamicParams = New-Object Management.Automation.RuntimeDefinedParameterDictionary
        $dynamicParams.Add('RequestTimeout', $RequestTimeOutParameter)

        $dynamicParams
    }

    process {
        $RequestTimeoutParameter | Format-List

        if ($PSBoundParameters.ContainsKey('UseDynamicParameterDefault2000')) {
            Write-Host "`$PSBoundParameters contains 'RequestTimeout'? $($PSBoundParameters.ContainsKey('RequestTimeout'))"
            Write-Host "`$RequestTimeout default: 2000"
            if ($PSBoundParameters.ContainsKey('RequestTimeout')) {
                # UPDATE: The following line should have used $PSBoundParameters to access the RequestTimeout parameter and has been corrected below
                #Write-Host "`$RequestTimeout = $RequestTimeout (Bound)"
                Write-Host "`$RequestTimeout = $($PSBoundParameters['RequestTimeout']) (Bound)"
            } else {
                # UPDATE: To be safe, also access $RequestTimeout here via $PSBoundParameters
                #Write-Host "`$RequestTimeout = $RequestTimeout (Default Value)"
                Write-Host "`$RequestTimeout = $($PSBoundParameters['RequestTimeout']) (Default Value)"
            }
        } else {
            Write-Host "`$PSBoundParameters contains 'RequestTimeout'? $($PSBoundParameters.ContainsKey('RequestTimeout'))"
            Write-Host "`$RequestTimeout default: 120000"
            if ($PSBoundParameters.ContainsKey('RequestTimeout')) {
                Write-Host "`$RequestTimeout = $($PSBoundParameters['RequestTimeout']) (Bound)"
            } else {
                # UPDATE: Again, use $PSBoundParameters
                #Write-Host "`$RequestTimeout = $RequestTimeout (UnBound, Default Value)"
                Write-Host "`$RequestTimeout = $($PSBoundParameters['RequestTimeout']) (UnBound, Default Value)"
            }
        }
    }
}

And now some tests executing the function (again, using PowerShell 5.1):

PS C:\> Test-DynamicParameter


Name          : RequestTimeout
ParameterType : System.Int32
Value         : 120000
IsSet         : True
Attributes    : {__AllParameterSets, System.Management.Automation.ValidateScriptAttribute}



$PSBoundParameters contains 'RequestTimeout'? False
$RequestTimeout default: 120000
$RequestTimeout =  (Unbound, Default Value)

PS C:\> Test-DynamicParameter -RequestTimeout 3000


Name          : RequestTimeout
ParameterType : System.Int32
Value         : 3000
IsSet         : True
Attributes    : {__AllParameterSets, System.Management.Automation.ValidateScriptAttribute}



$PSBoundParameters contains 'RequestTimeout'? True
$RequestTimeout default: 120000
$RequestTimeout = 3000 (Bound)

PS C:\> Test-DynamicParameter -UseDynamicParameterDefault2000

Name          : RequestTimeout
ParameterType : System.Int32
### UPDATE: Due to incorrect code, this line was wrong...
### Value         : 120000
Value         : 2000
IsSet         : True
Attributes    : {__AllParameterSets, System.Management.Automation.ValidateScriptAttribute}



$PSBoundParameters contains 'RequestTimeout'? False
$RequestTimeout default: 2000
$RequestTimeout =  (Default Value)

PS C:\> Test-DynamicParameter -UseDynamicParameterDefault2000 -RequestTimeout 3000


Name          : RequestTimeout
ParameterType : System.Int32
Value         : 3000
IsSet         : True
Attributes    : {__AllParameterSets, System.Management.Automation.ValidateScriptAttribute}



$PSBoundParameters contains 'RequestTimeout'? True
$RequestTimeout default: 2000
### UPDATE: This line is incorrect when the PowerShell function is corrected.
### $RequestTimeout = 3000 (Bound)
###
$RequestTimeout = 3000 (Bound)

PS C:\>

OK, so I learned a few things from this output. One is that I was trying to use $PSBoundParameters while constructing the dynamic parameter in order to set its default value (to either 2000, or 120000). However, this doesn't work, as the parameters have not yet been processed. (I was wrong, you can use $PSBoundParameters while constructing dynamic parameters.) What happens is the parameter is created, then the values sent into the cmdlet for the various parameters are bound. In the case where a value for the dynamic parameter is specified, the dynamic parameter's Value property is updated. In this sense, then, the Value property is not a default value for the dynamic parameter; it's the value for the parameter.

So, in my function where I try to set the "default" value of the dynamic parameter contingent upon the value of other (bound) parameters, this doesn't work and the value 120000 is always set as the initial value for the dynamic parameter. (Due to my coding error, this was true. But once the code is corrected, this statement is false.)

OK, but when I don't specify -RequestTimeout <n> to an invocation of the cmdlet, referring to $RequestTimeout in the cmdlet results in a null value. What gives? How do I get the default value I set on the parameter? That's easy. I still have access to the $RequestTimeoutParameter variable I used to build up the parameter definition. And as you can see in the output, I wrote that out and you can see the Value property is set. Furthermore, when -RequestTimeout <n> is specified, the $RequestTimeoutParameter.Value property is updated with the value passed in from the command invocation.

I hope this helps someone else out there.

like image 42
fourpastmidnight Avatar answered Sep 29 '22 20:09

fourpastmidnight