我正在尝试在Powershell脚本中嵌套DataTable。 考虑一个非常简单的数据表:
$somedt = New-Object System.Data.DataTable
[void]$somedt.Columns.Add('foo')
[void]$somedt.Columns.Add('bar')
[void]$somedt.Rows.Add('boo', 'bah')
[void]$somedt.Rows.Add('typed', 'better')
$somedt.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DataTable System.ComponentModel.MarshalByValueComponent
我想将此作为另一个数据表中的值。所以让我们创建一个:
$dt = New-Object System.Data.DataTable
[void]$dt.Columns.Add('Title')
$dtcol = New-Object System.Data.DataColumn
$dtcol.ColumnName = "TableData"
$dtcol.DataType = "System.Data.DataTable"
$dtcol.DefaultValue = New-Object System.Data.DataTable
[void]$dt.Columns.Add($dtcol)
[void]$dt.Rows.Add("Orange")
[void]$dt.Rows.Add("Red")
$dt | Format-Table -AutoSize
Title TableData
----- ---------
Orange {}
Red {}
我们验证:
$dt.Columns["TableData"].DataType
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DataTable System.ComponentModel.MarshalByValueComponent
和
$dt.Rows[1].TableData.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DataTable System.ComponentModel.MarshalByValueComponent
所以现在我验证$ somedt的类型并创建一个新的DataRow,它保存$ somedt,因为它是'TableData'值:
$somedt.gettype()
$myrow = $dt.NewRow()
$myrow["Title"] = 'Blue'
$myrow["TableData"] = $somedt
$myrow
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DataTable System.ComponentModel.MarshalByValueComponent
值的类型与列类型不匹配无法存储在TableData列中。预期的类型是DataTable。 在行:5 char:1 + $ myrow [“TableData”] = $ somedt + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo:OperationStopped:(:) [],ArgumentException + FullyQualifiedErrorId:System.ArgumentException
Title : Blue
TableData : {}
那为什么会失败呢?似乎Powershell采用DataTable并将其转换为DataRows集合。我怎样才能防止这种情况发生?
它是从函数返回值时使用的“逗号技巧”的反转,或者似乎是这样。
我可以从'TableData'列中删除DataType限制,但这只会导致默认的空DataTables没有添加,并且嵌套的datatble被添加为DataRows的集合
Title TableData
----- ---------
Orange
Red
Blue System.Data.DataRow System.Data.DataRow
我真的希望能够使用DataTables的一些功能,但我还需要嵌套。如果不是,我需要重新思考/重构一下。 DataColumn.DataType上的文档没有提到这些复杂的类型,但是这部分代码似乎最初工作正常。
我做错了吗?或者这根本不可能?
P.S。这需要在Powershell v3.0 / .Net 4.5中运行
答案 0 :(得分:0)
在摆弄了一点之后,我发现如果我这样调用它,Add()方法不会弄乱数据类型:
$dt.Rows.Add("Blue",$somedt) | Out-Null
因此深入研究文档,我完全忽视了这一点: How to: Add Rows to a DataTable
因此,因为它们是类型化的数据列,所以应该使用点表示法来解决它们。不幸的是,尝试以下操作也失败了:
$somedt.gettype()
$myrow = $dt.NewRow()
$myrow.Title = 'Blue'
$myrow.TableData = $somedt
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DataTable System.ComponentModel.MarshalByValueComponent
Exception setting "TableData": "Type of value has a mismatch with column typeCouldn't store <System.Data.DataRow System.Data.DataRow> in TableData Column. Expected type is DataTable."
At line:5 char:1
+ $myrow.TableData = $somedt
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : CatchFromBaseAdapterSetValue
然而,根据Adding Data to a DataTable
将一个值类型为Object的值传递给Add方法会在表中创建一个新行,并将其列值设置为对象数组中的值。请注意,数组中的值将按顺序与列匹配,具体取决于它们在表中的显示顺序。
这样做似乎有效:
$dt.Rows.Add('Blue', $somedt) | Out-Null
$dt | Format-Table -AutoSize
$dt.rows[2].TableData.gettype()
结果:
Title TableData
----- ---------
Orange {}
Red {}
Title TableData
----- ---------
Orange {}
Red {}
Blue {System.Data.DataRow, System.Data.DataRow}
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DataTable System.ComponentModel.MarshalByValueComponent
我并不喜欢这种方式,因为它取决于列的顺序。它还将值显示为数据行的集合,这让我感到很奇怪,但GetType()表明它确实是一个DataTable。
最后,我可以做类似的事情:
$rccol = New-Object System.Data.DataColumn
$rccol.ColumnName = "RowCount"
[void]$dt.Columns.Add($rccol)
foreach ($row in $dt.Rows) {
$row.RowCount = $row.TableData.Rows.Count
}
$dt | Format-Table -AutoSize
然后导致:
Title TableData RowCount
----- --------- --------
Orange {} 0
Red {} 0
Blue {System.Data.DataRow, System.Data.DataRow} 2
我仍然想知道为什么它使用NewRow()过程添加DataTable失败,所以我可以显式地将值添加到该列,而不是依赖列顺序(可能随时间变化)。 / p>
根据PetSerAl的建议更新,更改$ somedt的实例化确实有效,尽管我不明白为什么要精确。当作为参数或类似的东西传入时,BaseObjects会以某种方式逃避集合展开吗?
无论如何,这很好用:
$somedt = (New-Object System.Data.DataTable).PSObject.BaseObject
[void]$somedt.Columns.add('foo')
[void]$somedt.Columns.Add('bar')
[void]$somedt.Rows.Add('boo', 'bah')
[void]$somedt.Rows.Add('typed', 'better')
$dt = New-Object System.Data.DataTable
[void]$dt.Columns.Add('Title')
$dtcol = New-Object System.Data.DataColumn
$dtcol.ColumnName = "TableData"
$dtcol.DataType = "System.Data.DataTable"
$dtcol.DefaultValue = New-Object System.Data.DataTable
[void]$dt.Columns.Add($dtcol)
[void]$dt.Rows.Add("Orange")
[void]$dt.Rows.Add("Red")
$dt | Format-Table -AutoSize
$myrow = $dt.NewRow()
$myrow.Title = 'Blue'
$myrow.TableData = $somedt
$dt.Rows.Add($myrow)
$dt | Format-Table -AutoSize
$dt.rows[2].TableData.gettype()
输出:
Title TableData
----- ---------
Orange {}
Red {}
Title TableData
----- ---------
Orange {}
Red {}
Blue {System.Data.DataRow, System.Data.DataRow}
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DataTable System.ComponentModel.MarshalByValueComponent
答案 1 :(得分:0)
由于New-Object
cmdlet的PSObject
对象的PowerShell包装结果导致您的异常发生:
$DataTable = New-Object System.Data.DataTable
$DataTableUnwrapped = $DataTable.PSObject.BaseObject
[Type]::GetTypeArray(($DataTable,$DataTableUnwrapped)).FullName
此命令将打印:
System.Management.Automation.PSObject
System.Data.DataTable
在某些情况下,PowerShell会自动解包PSObject
:
[Object]::ReferenceEquals($DataTable,$DataTableUnwrapped) #return True
在其他情况下,例如分配给属性Object
的属性或索引器:
$myrow["TableData"] = $somedt
$myrow.TableData = $somedt
解包不会发生,属性或索引器访问者获得PSObject
实例。
由于System.Data
API不支持PowerShell,因此它不知道如何解包PSObject
或将其转换为DataTable
。因此,它拒绝将PSObject
存储在键入为DataTable
的列中。