【问题标题】:Powershell nested DataTablePowershell 嵌套数据表
【发布时间】:2016-04-19 09:22:15
【问题描述】:

我正在尝试将 DataTable 嵌套在 Powershell 脚本中。 考虑一个非常简单的数据表:

$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 字符: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 上的文档没有提到这些复杂类型,但那部分代码最初似乎可以正常工作。

我做错了吗?或者这根本不可能?

附:这需要在 Powershell v3.0 / .Net 4.5 中工作

【问题讨论】:

  • $somedt = (New-Object System.Data.DataTable).PSObject.BaseObject
  • @PetSerAl 就是这样!谢谢!。您能否将其放在答案中,以便我可以将其标记为这样?我现在正在阅读这个 baseobject 属性,但已经过测试,它可以按预期工作。非常感谢!

标签: .net powershell datatable


【解决方案1】:

您的异常是由于 PowerShell 将 New-Object cmdlet 的结果包装到 PSObject 对象中:

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

【讨论】:

    【解决方案2】:

    经过一番折腾后,我发现 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 时会失败,因此我可以显式地将值添加到该列,而不是依赖列顺序(这可能会随着时间而改变)。

    根据 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-21
      • 2018-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-21
      相关资源
      最近更新 更多