如果没有显式强制转换,对象将被包装在PSObject中

时间:2014-01-27 19:29:52

标签: powershell casting powershell-v3.0 psobject

当显式地转换我的新对象时(参见第一行代码),我新创建的EntityReference被存储而不包含在PSObject中,因此序列化它可以正常工作:

$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()

然而,当我不使用演员时:

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

Powershell在我想要序列化时抱怨:
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.

现在,我读过Why does Get-Date seem to return DateTime objects, but the BinarySerializer indicate it returns a PSObject?,看来这是同样的问题.... 除了我使用Powershell 3.0 $PSVersionTable.psversion生成版本3.0.-1.-1)并且该帖子中的'错误'代码片段在我的Powershell环境中正常工作... < / p>

在那篇文章中,有人建议Powershell 3的新的基于DLR的引擎不再引起这些问题,那么他们是否过于乐观,或者我遇到了其他问题?


编辑:以下代码产生相同的行为,而不依赖于CRM SDK。使用强制转换,powershell抱怨无法序列化System.UriBuilder,而没有强制转换,它抱怨获得System.Management.Automation.PSObject实例:

# $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()

1 个答案:

答案 0 :(得分:3)

是的,这是另一个解决错误的问题,虽然在我思考之后,我意识到PowerShell团队已经知道了这一点,并且很可能会暂时“无法修复”。在你诅咒它们之前,请考虑一下:当psobjects(隐式键入的所有内容)被传递给.NET方法时,它们被绑定器解包但它不会递归到枚举或检查属性以查看是否需要打开包装。坦率地说,它现在无法知道输入包含psobject的对象(例如)的属性是否应该被解包,因此它什么都不做。如果需要,只打开第一级实例。

我认为真正的错误 - 并且值得登录connect.microsoft.com/powershell - 是指uribuilder实例在分配给哈希表键时未解包。 在我看来,这将是一个明确的情况

黄金法则:如果分配目标是对象,PowerShell将永远不会解包psobject。

因此,解决方法是在创建时或在分配点进行强制转换:

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

New-Object cmdlet是包装的原因。 cmdlet将实例传递给PSCmdlet.WriteObject后,将应用包装器。

希望这有帮助。