2016-04-19 2 views
1

Я пытаюсь вставить DataTable в сценарий 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                                       

Я хотел бы, чтобы это как значение в другом DataTable. Итак, давайте создадим один:

$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, как это " Значение таблицыДата:

$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. Как я могу предотвратить это?

Это своего рода обратная сторона «запятой», используемой при возврате значений из функции или, как кажется.

я могу снять ограничение DataType из столбца «TableData», но это как раз и приводит к дефолту пустых 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

'$ somedt = (New-Object System.Data.DataTable) .PSObject.BaseObject' – PetSerAl

+0

@PetSerAl Вот и все! Благодаря!. Не могли бы вы ответить на него, чтобы я мог отметить это как таковое? Я читаю об этом свойстве baseobject прямо сейчас, но уже протестирован и работает так, как ожидалось. Большое спасибо! – mvdwrd

ответ

0

Ваше исключение происходит из-за PowerShell оберточной результате New-Object командлета в 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 

разворачивание не происходит и собственности или indexer accessor получил PSObject экземпляр.

Как System.Data API не знает PowerShell, он не знает, как развернуть PSObject или преобразовать его в DataTable. Таким образом, он отказывается хранить PSObject в столбце, напечатанном как DataTable.

0

После пустячный вокруг немного больше, я обнаружил, что метод Add() не связывайтесь с типом данных, если я назвал это так:

$dt.Rows.Add("Blue",$somedt) | Out-Null 

так копаться в документации, я полностью игнорировать это: How to: Add Rows to a DataTable

Итак, потому что они набираются datacolumns они должны быть рассмотрены как таковые, используя точечную нотацию.Попытка следующего, к сожалению, также не:

$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

Проходя массив значений, набранный как объект, к методу 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 

Я не очень-то фантазии таким образом, как это зависит от того, из колонны. Он также отображает значение как коллекцию datarows, которая немного меня удивляет, но 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  

Тем не менее, я хотел бы знать, почему он не на добавление DataTable с помощью NewRow() поэтому я могу явно добавить значение в этот столбец вместо того, чтобы полагаться на порядок столбцов (который может меняться со временем).

UPDATE из предложения 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 
Смежные вопросы