Pregunta

When explicitly casting my new object (see the first line of code), my newly created EntityReference gets stored without being wrapped in a PSObject, and so serializing it works just fine:

$entityRef1 = [Microsoft.Xrm.Sdk.EntityReference](new-object Microsoft.Xrm.Sdk.EntityReference("businessunit", [System.Guid]::NewGuid()))
$unit = new-object Microsoft.Xrm.Sdk.Entity("businessunit")
$unit.Attributes["parentbusinessunitid"] = $entityRef1
$unit.Attributes["parentbusinessunitid"].GetType()  # Produces "EntityReference"

# Serialize $unit including the entityRef, and write its xml representation
$serializer = New-Object System.Runtime.Serialization.DataContractSerializer($unit.GetType())
$stream = New-Object System.IO.MemoryStream
$serializer.WriteObject($stream, $unit)
$stream.Flush()
$stream.Seek(0, [System.IO.SeekOrigin]::Begin)
$reader = New-Object System.IO.StreamReader($stream)
$reader.ReadToEnd()

However, when I don't use the cast:

$entityRef1 = (new-object Microsoft.Xrm.Sdk.EntityReference("businessunit", [System.Guid]::NewGuid()))

PowerShell complains when I want to serialize it: Exception calling "WriteObject" with "2" argument(s): "Type 'System.Management.Automation.PSObject' with data contract name 'PSObject:http://schemas.datacontract.org/2004/07/System.Management.Automation' is not expected.

Now, I've read Why does Get-Date seem to return DateTime objects, but the BinarySerializer indicate it returns a PSObject?, and it would appear that this is the same problem.... except that I use PowerShell 3.0 ($PSVersionTable.psversion produces version 3.0.-1.-1) and that the 'faulty' code fragment from that post works fine in my PowerShell environment...

In that post it is suggested that the new DLR-based engine from PowerShell 3 should no longer cause these problems, so were they being too optimistic about that, or have I run into something else?


Edit: The code below produces the same behaviour without being dependant on the CRM SDK. With the cast, PowerShell complains about not being able to serialize System.UriBuilder, whereas without the cast, it complains about getting a System.Management.Automation.PSObject instance:

# $uriBuilder = [UriBuilder](New-Object UriBuilder)
$uriBuilder = (New-Object UriBuilder)

$dict = new-object Hashtable
$dict["mykey"] = $uriBuilder
$dict["mykey"].GetType()  # Produces "UriBuilder"

# Serialize $dict including the uriBuilder, and write its xml representation
$serializer = New-Object System.Runtime.Serialization.DataContractSerializer($dict.GetType())
$stream = New-Object System.IO.MemoryStream
$serializer.WriteObject($stream, $dict)
$stream.Flush()
$stream.Seek(0, [System.IO.SeekOrigin]::Begin)
$reader = New-Object System.IO.StreamReader($stream)
$reader.ReadToEnd()
¿Fue útil?

Solución

Yes, this is another PSObject unwrapping bug, although after I pondered it, I realised it is known about by the PowerShell team and will likely remain a "won't fix" for a while.

Before you curse them, think about this: When PSObject's (everything implicitly typed is) are passed to .NET methods, they are unwrapped by the binder, but it does not recurse into enumerables or examine properties to see if things need to be unwrapped or not. Frankly, it cannot know at this point whether a property typed as object (for example) containing a PSObject, should be unwrapped or not, so it does nothing. Only first-level instances are unwrapped, if required.

I think the real bug here - is that the uribuilder instance is not unwrapped when it is assigned to the hashtable key.

The golden rule: PowerShell will never unwrap the PSObject if the assignment target is object.

So, the workaround is either cast at the creation, or at the assignment point:

$ht["foo"] = [uribuilder]$builder

The New-Object cmdlet is the cause of the wrapping. Once the cmdlet passes the instance to PSCmdlet.WriteObject, the wrapper is applied.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top