Author Posts

August 31, 2015 at 7:17 pm

Hello all. I am trying to understand the below behavior. The below code is very simple and made just to present the behavior, not to be useful code.

First I have a function that generated a collection of Custom PSObjects and then uses that collection as a property for another Custom PSObject that is sent out to the pipeline along with 1 string property and 1 DateTime Object property.

function Test1 {
    $collection = @()
    foreach ($i in 0..10) {
        $collection += New-Object -TypeName PSObject -Property @{value1 = "1:$i"; value2 = "2:$i"; value3 = "3:$i"}
    }#foreach

    New-Object -TypeName PSObject -Property @{name = "Collection"; value = $collection; date = (Get-Date)}
}#function

The result of this function to standard output is exactly what I would expect. A date property with the date, a name property with a string of "Collection", and a value property with a collection of values


PS C:\> test1

date name value
---- ---- -----
8/31/2015 10:02:45 PM Collection {@{value1=1:0; value2=2:0; v...

I then have a second function that I want to accept the object that has been output by the first function as input for the second function.

function Test2 {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
        [String]$Name,
        [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
        $Value,
        [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
        $Date
    )
    Begin{}
    Process {
        Write-Host "----------------------------------------------------"
        Write-Host $Name
        Write-Host "----------------------------------------------------"
        Write-Host $Value
        Write-Host "----------------------------------------------------"
        Write-Host $Date
        Write-Host "----------------------------------------------------"
        Write-Host $Value.Value
        Write-Host "----------------------------------------------------"
        Write-Host $Date.Date
        Write-Host "----------------------------------------------------"
    }#process
    End{}
}#function

Below are the results of piping test1 to test2 and where I become confused.


PS C:\> test1 | test2
----------------------------------------------------
Collection
----------------------------------------------------
@{date=8/31/2015 10:07:32 PM; name=Collection; value=System.Object[]}
----------------------------------------------------
@{date=8/31/2015 10:07:32 PM; name=Collection; value=System.Object[]}
----------------------------------------------------
@{value1=1:0; value2=2:0; value3=3:0} @{value1=1:1; value2=2:1; value3=3:1} @{value1=1:2; value2=2:2; value3=3:2} @{value1=1:3; value2=2:3; value3=3:3} @{value1=1:4; value2=2:4; value3=3:4} @{value1=1:5; value2=2:5; value3=3:5} @{value1=1:6; value2=2:6; value3=3:6} @{value1=1:7; value2=2:7; value3=3:7} @{value1=1:8; value2=2:8; value3=3:8} @{value1=1:9; value2=2:9; value3=3:9} @{value1=1:10; value2=2:10; value3=3:10}
----------------------------------------------------
8/31/2015 10:07:32 PM
----------------------------------------------------

You can see by the results that $data and $value seem to contain the complete object from the pipeline rather than just the value that they were set to by the new-object in test1. This is confirmed by the fact that I can get the property I want from the variable that should contain the property value by using ..

Lastly while I am very interested to know why this behaves in this manner, the ultimate goal is to do things correctly. Since this is the behavior, it is obviously not the way to handle this scenario. So what is the correct methodology for passing a collection of values as a property that can be passed through the pipeline to another function?

Thanks

August 31, 2015 at 9:25 pm

The solution is to remove the "ValueFromPipeline=$True". When parameter binding occurs it tries to bind parameters by value first. Since your Value and Date parameters are not strongly typed, and you are enabling value from pipeline, it will bind the whole object to the parameter.

The other solution would be to strongly type your parameters, which works for date eg
[DateTime]$Date

However, since $Value would be a PSCustomObject, and the parent object that you're passing down the pipeline is also a PSCustomObject, this will still bind the whole object to the $Value parameter rather than just the $_.Value object.

September 1, 2015 at 6:56 am

Peter, thanks that is exactly what I needed. A little further investigation and I found that I can get the correct data by both dropping the "ValueFromPipeline=$True" parameter, and by strong typing my collection as [Object]. I did try strong typing previously, however since I had ValueFromPipeline=$True still defined, I got the same result for the Custom PowerShell Object. The below changes to the Test2 function now work as expected.

function Test2 {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
        [String]$Name,
        [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
        [Object]$Value,
        [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
        [DateTime]$Date
    )
    Begin{}
    Process {
        Write-Host "----------------------------------------------------"
        Write-Host $Name
        Write-Host "----------------------------------------------------"
        Write-Host $Value
        Write-Host "----------------------------------------------------"
        Write-Host $Date
        Write-Host "----------------------------------------------------"
        Write-Host $Value.Value
        Write-Host "----------------------------------------------------"
        Write-Host $Date.Date
        Write-Host "----------------------------------------------------"
    }#process
    End{}
}#function


PS C:\Users\csmith> Test1 | Test2
----------------------------------------------------
Collection
----------------------------------------------------
@{value1=1:0; value2=2:0; value3=3:0} @{value1=1:1; value2=2:1; value3=3:1} @{value1=1:2; value2=2:2; value3=3:2} @{value1=1:3; value2=2:3; value3=3:3} @{value1=1:4; value2=2:4; value3=3:4} @{value1=1:5; value2=2:5; value3=3:5} @{value1=1:6; value2=2:6; value3=3:6} @{value1=1:7; value2=2:7; value3=3:7} @{value1=1:8; value2=2:8; value3=3:8} @{value1=1:9; value2=2:9; value3=3:9} @{value1=1:10; value2=2:10; value3=3:10}
----------------------------------------------------
9/1/2015 9:54:12 AM
----------------------------------------------------

----------------------------------------------------
9/1/2015 12:00:00 AM
----------------------------------------------------