Странное поведение в функции PowerShell, возвращающей DataSet/DataTable
Это сводит меня с ума. У меня есть библиотека из нескольких сценариев, которая содержит следующую функцию:
function lib_open_dataset([string] $sql) {
$ds = new-object "System.Data.DataSet"
$da = new-object "System.Data.SqlClient.SqlDataAdapter" ($sql, $_conn_string)
$record_count = $da.Fill($ds)
return $ds
}
Это называется почти везде, и это работает просто отлично, за исключением того, что я обычно должен делать это:
$ds = lib_open_dataset($some_sql)
$table = $ds.Tables[0]
foreach ($row in $table.Rows) {
# etc
}
Поэтому я создал новую простую функцию-обертку, чтобы избежать дополнительного шага разыменования первой таблицы:
function lib_open_table([string] $sql) {
$ds = lib_open_dataset $sql
return $ds.Tables[0]
}
Проблема в том, что по какой-то причине здесь возвращается набор строк таблицы, а не сама таблица. Это вызывает foreach
Цикл строки, написанный как выше, завершается с ошибкой "Невозможно индексировать в нулевой массив". исключение. После долгих проб и ошибок я понял, что это работает:
foreach ($row in $table) {
# etc
}
Обратите внимание на разницу между $table.Rows
и просто $table
в foreach
заявление. Это работает Так как $table
на самом деле указывает на коллекцию строк. Если заявление
return $ds.Tables[0]
предположительно правильно, почему функция возвращает дочернюю коллекцию объекта таблицы вместо самой таблицы?
Я предполагаю, что есть кое-что в способах работы функций Powershell, которые вызывают это, очевидно, но я не могу понять, что.
3 ответа
Вы можете использовать оператор запятой, чтобы обернуть коллекцию строк в массив так, чтобы при развертывании массива вы получали исходную коллекцию строк, например:
function lib_open_table([string] $sql) {
$ds = lib_open_dataset $sql
return ,$ds.Tables[0]
}
По сути, вы не можете запретить PowerShell развертывать массивы / коллекции. Лучшее, что вы можете сделать, это обойти это поведение, поместив массив / коллекцию в другой, отдельный элемент массива.
Особые случаи PowerShell для DataTable внутри. Он не реализует какие-либо обычные подозрительные интерфейсы, такие как ICollection, IList или IEnumerable, которые обычно запускают развертывание. Вы можете немного покопаться в этом:
PS> $dt = new-object data.datatable
PS> $dt -is [collections.ienumerable]
False
Еще:
PS> $e = [management.automation.languageprimitives]::GetEnumerator($dt)
PS> $e.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
False False RBTreeEnumerator System.ValueType
-Oisin
О да, я тоже боролся с этим, пока не получил этот пост..(tnxs Кит!)
2 вещи, на которых вы действительно должны сосредоточиться: а) добавьте возвращаемый объект запятой действительно б) когда вы заполняете свой адаптер, убедитесь, что вы либо присвоили результат переменной (утилизируемой), либо выполнили Out-Null
Я не делал Out-Null и даже с добавленной запятой, я продолжал возвращать коллекцию (item 0= количество строк в запросе, item1= datatable) До некоторой степени сводил меня с ума, пока я не выбрал Out-null выходной параметр
Очень странно, ИМХО, поскольку я специально просил вернуть данные, но продолжал получать коллекцию обратно, даже с "," перед
function Oracleconnection
{
process
{
trap
{
Write-Host "error occured on oracle connection"
Write-Host $_
continue
}
[System.Reflection.Assembly]::LoadWithPartialName(“System.Data.OracleClient”) | out-null
$connection = new-object system.data.oracleclient.oracleconnection( `
"Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=myhost.host)(PORT=1800)) `
(CONNECT_DATA=(SERVICE_NAME=myservicename)));User Id=myid;Password=mypassword;");
$query = "SELECT country, asset FROM table "
$set = new-object system.data.dataset
$adapter = new-object system.data.oracleclient.oracledataadapter ($query, $connection)
$adapter.Fill($set) | Out-Null
$table = new-object system.data.datatable
$table = $set.Tables[0]
return ,$table
}
}