2009-12-16 4 views
18

Cela me rend fou. J'ai une source I de la bibliothèque à partir de plusieurs scripts qui contient la fonction suivante:Comportement étrange dans la fonction PowerShell renvoyant 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 
} 

Ceci est appelé un peu partout et il fonctionne très bien, sauf que je dois normalement faire ceci:

$ds = lib_open_dataset($some_sql) 
$table = $ds.Tables[0] 
foreach ($row in $table.Rows) { 
    # etc 
} 

Je créé une nouvelle fonction d'emballage simple pour éviter l'étape supplémentaire de déréférencement la première table:

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

le problème est que ce qui est de retour d'ici est le Collection de lignes de la table pour une raison quelconque, pas la table elle-même. Cela provoque l'échec de la boucle de ligne foreach écrite comme ci-dessus avec un «impossible d'indexer dans un tableau null». exception. Après beaucoup d'essais et d'erreurs que j'ai compris cela fonctionne:

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

Notez la différence entre $table.Rows et juste $table dans la déclaration foreach. Cette fonctionne. Parce que $table pointe vers la collection Rows. Si la déclaration

return $ds.Tables[0] 

est censé être correct, pourquoi la fonction renvoie une collection enfant de l'objet de la table au lieu de la table elle-même?

Je suppose qu'il y a quelque chose dans la façon dont fonctionnent les fonctions de Powershell qui cause cela évidemment, mais je n'arrive pas à comprendre quoi.

Répondre

21

Vous pouvez utiliser l'opérateur virgule pour envelopper les lignes collection dans un tableau de sorte que lorsque le tableau est déroulé vous liquidez avec les lignes d'origine collection par exemple:

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

Essentiellement, vous ne pouvez pas empêcher PowerShell de dérouler les tableaux/collections. Le mieux que vous puissiez faire est de contourner ce comportement en enveloppant le tableau/collection dans un autre tableau d'éléments uniques.

+0

impressionnant, qui a fonctionné. C'est bizarre, mais ça a marché :) Merci beaucoup Keith. – kprobst

+0

Ce que je ne comprends pas, c'est où le tableau est déroulé. Et pourquoi seulement la collection Rows, pourquoi pas d'autres propriétés? Pourquoi pas, par exemple Propriété Columns – stej

+0

Je ne suis pas un gourou DataSet mais je me demande si la table de données retournée est de type TypedTableBase qui est énumérable sur T où T est DataRow. –

12

PowerShell spécial-cas le DataTable en interne. Il n'implémente aucune des interfaces suspectes habituelles comme ICollection, IList ou IEnumerable qui déclenchent normalement le déroulement. Vous pouvez creuser dans ce un peu avec:

PS> $dt = new-object data.datatable 
PS> $dt -is [collections.ienumerable] 
False 

Et pourtant:

PS> $e = [management.automation.languageprimitives]::GetEnumerator($dt) 
PS> $e.gettype() 

IsPublic IsSerial Name          BaseType 
-------- -------- ----          -------- 
False False RBTreeEnumerator       System.ValueType 

-Oisin

+0

Merci Oisin, je pense que cela clarifie ce qui se passe. Qu'est-ce que je me demande est _why_ PS fait cela du tout? – kprobst

4

Oh oui, j'ai struggeling avec celui-ci aussi jusqu'à ce que je suis arrivé ce post .. (tnxs Keith!)

2 choses que vous devez vous concentrer bien sur a) votre objet retourné précédez avec la virgule en effet b) lorsque vous remplissez votre carte, assurez-vous soit assigner la variable le résultat à un (disposalble) ou faire une Out-Null

je ne faisais pas l'Out-Null et même avec une virgule Prepended, je continué à obtenir une collection en arrière (point 0 = nombre de lignes de la requête, item1 = datatable) Drove mon un peu fou jusqu'à ce que j'ai choisi le paramètre Out-null.

à mon humble avis Très bizarre, comme je demande spécifiquement de retourner datatable mais a gardé revenir la collection, même avec le « » devant

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