PowerShell函数中的奇怪行为返回DataSet / DataTable

时间:2009-12-16 22:15:51

标签: database powershell

这让我发疯了。我有一个来自多个脚本的库,它包含以下函数:

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]
}

问题是,由于某些原因,从这里返回的是表的 Rows集合,而不是本身。这会导致上面写的foreach行循环失败并显示“无法索引到空数组”。例外。经过多次反复试验后,我发现了这一点:

foreach ($row in $table) {
    # etc
}

请注意$table.Rows语句中$tableforeach之间的差异。这有效。因为$table实际指向Rows集合。如果声明

return $ds.Tables[0]

应该是正确的,为什么函数返回表对象的子集合而不是表本身?

我猜测Powershell功能的工作原理显然导致了这一点,但我无法弄清楚是什么。

3 个答案:

答案 0 :(得分:23)

您可以使用逗号运算符将行集合包装在一个数组中,以便在展开数组时结束原始行集合,例如:

function lib_open_table([string] $sql) {
    $ds = lib_open_dataset $sql    
    return ,$ds.Tables[0]
}

基本上,您无法阻止PowerShell展开数组/集合。您可以做的最好的事情就是通过将数组/集合包装在另一个单个元素数组中来解决这种行为。

答案 1 :(得分:12)

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

答案 2 :(得分:4)

哦,是的,在我收到这篇文章之前,我也一直在努力。(tnxs Keith!)

你需要关注的两件事 a)确实用逗号添加返回的对象 b)当您填充适配器时,请确保将结果分配给(可处理的)变量执行Out-Null

我没有做Out-Null,即使使用前置逗号,我仍然收到一个回合(第0项=查询中的行数,item1 =数据表) 推开我有点疯狂,直到我选择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
  }
}