PowerShell远程传递System.Data.DataTable遇问题,求简化或无转换方案
PowerShell远程传递System.Data.DataTable的问题与解决
问题场景
尝试通过Invoke-Command向远程主机传递System.Data.DataTable类型变量时,两种传递方式均报错,且在PowerShell 5.1和7.2版本中问题一致。
尝试1:使用ArgumentList传递
function Test1 { param ( [System.Data.DataTable] $Table ) process { [PSCustomObject]$OptionsInvokeCommand = @{ ComputerName = '192.168.0.5' Authentication = 'Negotiate' ArgumentList = [System.Data.DataTable]$Table } write-host $Table.GetType() write-host $OptionsInvokeCommand.ArgumentList[0].GetType() [System.Management.Automation.ScriptBlock]$ScriptBlock = { param ( [System.Data.DataTable]$Table ) process { write-host $Table.GetType() } } Invoke-Command @OptionsInvokeCommand -ScriptBlock $ScriptBlock } } $Table = New-Object System.Data.DataTable Test1 -Table $Table
执行报错:
Cannot process argument transformation on parameter 'Table'. Cannot convert the "System.Collections.ArrayList" value of type "System.Collections.ArrayList" to type "System.Data.DataTable". + CategoryInfo : InvalidData: (:) [], ParameterBindin...mationException + FullyQualifiedErrorId : ParameterArgumentTransformationError + PSComputerName : 192.168.0.5
尝试2:使用$using:传递变量
function Test2 { param ( [System.Data.DataTable] $Table ) process { [PSCustomObject]$OptionsInvokeCommand = @{ ComputerName = '192.168.0.5' Authentication = 'Negotiate' } write-host $Table.GetType() [System.Management.Automation.ScriptBlock]$ScriptBlock = { process { [System.Data.DataTable]$Table = $using:Table write-host $Table.GetType() } } Invoke-Command @OptionsInvokeCommand -ScriptBlock $ScriptBlock } } $Table = New-Object System.Data.DataTable Test2 -Table $Table
执行报错:
Cannot process argument transformation on parameter 'Table'. Cannot convert the "System.Collections.ArrayList" value of type "System.Collections.ArrayList" to type "System.Data.DataTable". + CategoryInfo : InvalidData: (:) [], ParameterBindin...mationException + FullyQualifiedErrorId : ParameterArgumentTransformationError + PSComputerName : 192.168.0.5
问题原因
PowerShell远程传输时,会自动将System.Data.DataTable序列化为XML格式,在远程主机反序列化后,原类型信息丢失,被转换为System.Collections.ArrayList,无法直接转回DataTable,这是PowerShell远程序列化机制的已知限制。
现有手动序列化实现
通过手动将DataTable序列化为带Schema的XML,传递后再反序列化还原,实现了类型保留,但代码依赖临时文件,较为繁琐:
方案1:ArgumentList传递XML字符串
function Test1 { param ( [System.Data.DataTable] $Table ) process { [string]$TmpPath = New-TemporaryFile $Table.WriteXml($TmpPath, [Data.XmlWriteMode]::WriteSchema) [string[]]$XmlString = Get-content $TmpPath Remove-Item -Force $TmpPath [PSCustomObject]$OptionsInvokeCommand = @{ ComputerName = '192.168.0.5' Authentication = 'Negotiate' ArgumentList = ,[string[]]$XmlString } write-host $Table.GetType() [System.Management.Automation.ScriptBlock]$ScriptBlock = { param ( [string[]]$XmlString ) process { [string]$TmpPath = New-TemporaryFile Out-File -InputObject $XmlString -FilePath $TmpPath -Encoding utf8 $Ds = New-Object Data.DataSet $Ds.ReadXml($TmpPath, [Data.XmlReadMode]::ReadSchema) $Table = $Ds.Tables[0] Remove-Item -Force $TmpPath write-host $Table.GetType() } } [void](Invoke-Command @OptionsInvokeCommand -ScriptBlock $ScriptBlock) } } $Table = New-Object System.Data.DataTable NameOfTable Test1 -Table $Table
方案2:$using:传递XML字符串
function Test2 { param ( [System.Data.DataTable] $Table ) process { [string]$TmpPath = New-TemporaryFile $Table.WriteXml($TmpPath, [Data.XmlWriteMode]::WriteSchema) [string[]]$XmlString = Get-content $TmpPath Remove-Item -Force $TmpPath [PSCustomObject]$OptionsInvokeCommand = @{ ComputerName = '192.168.0.5' Authentication = 'Negotiate' } write-host $Table.GetType() [System.Management.Automation.ScriptBlock]$ScriptBlock = { process { [string[]]$XmlString = $using:XmlString [string]$TmpPath = New-TemporaryFile Out-File -InputObject $XmlString -FilePath $TmpPath -Encoding utf8 $Ds = New-Object Data.DataSet $Ds.ReadXml($TmpPath, [Data.XmlReadMode]::ReadSchema) $Table = $Ds.Tables[0] Remove-Item -Force $TmpPath write-host $Table.GetType() } } [void](Invoke-Command @OptionsInvokeCommand -ScriptBlock $ScriptBlock) } } $Table = New-Object System.Data.DataTable NameOfTable Test2 -Table $Table
简化方案与结论
1. 简化手动序列化实现(无需临时文件)
可以直接在内存中完成XML的序列化与反序列化,省去磁盘操作,代码更简洁高效:
function Send-DataTableRemotely { param( [Parameter(Mandatory)] [System.Data.DataTable]$Table, [Parameter(Mandatory)] [string]$ComputerName ) # 内存中序列化DataTable为带Schema的XML字符串 $xmlWriter = New-Object System.IO.StringWriter $Table.WriteXml($xmlWriter, [System.Data.XmlWriteMode]::WriteSchema) $xmlContent = $xmlWriter.ToString() $xmlWriter.Dispose() # 远程执行反序列化并处理 Invoke-Command -ComputerName $ComputerName -Authentication Negotiate -ScriptBlock { param($XmlContent) $xmlReader = New-Object System.IO.StringReader($XmlContent) $ds = New-Object System.Data.DataSet $ds.ReadXml($xmlReader, [System.Data.XmlReadMode]::ReadSchema) $xmlReader.Dispose() $remoteTable = $ds.Tables[0] Write-Host "远程端DataTable类型: $($remoteTable.GetType().FullName)" # 在此添加对remoteTable的业务操作 return $remoteTable } -ArgumentList $xmlContent } # 测试示例 $testTable = New-Object System.Data.DataTable "TestTable" $testTable.Columns.Add("ID", [int]) $testTable.Rows.Add(1) Send-DataTableRemotely -Table $testTable -ComputerName '192.168.0.5'
2. 是否存在无需转换的传递方法?
没有。PowerShell远程使用的CLIXML序列化机制无法保留System.Data.DataTable的完整类型信息,传输后必然会被转换为ArrayList,因此必须通过手动序列化/反序列化的方式来保留类型。
内容的提问来源于stack exchange,提问作者Vladimir1211
相关产品推荐
相关产品推荐

