Powershell嵌套DataTable

时间:2016-04-19 09:22:16

标签: .net powershell datatable

我正在尝试在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中运行

2 个答案:

答案 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的列中。