1 Objectmodel der DataSet-Klasse


DataTable Klasse
2.1 Erstellen von Tabellen (Übersicht)
2.1.1 Von einem DataSet unäbhängige Tabelle erstellen
          Beispiel 1a: Erstellen einer einfachen Tabelle mit einer Spalte und wenigen Datensätzen
          Beispiel 1b: Erstellen einer Tabelle mit Spalten und im Skript definierten Datensätzen
          Beispiel 2: Ausgabe von Get-Process in DataTable einlesen
          Beispiel 3: Einlesen einer CSV-Datei in eine DataTable
          Beispiel 3a: Einlesen einer CSV-Datei in eine DataTable mit allen Spalten
          Beispiel 3b: Einlesen einer CSV-Datei in eine DataTable mit ausgewählten Spalten

2.1.2 neue (weitere) Tabellen in einer DataTableCollection erstellen
          Beispiel 1: Erzeugen einer DataTableCollection
2.2 Fill-Methode des DataAdapters

2.3 Direkter Zugriff auf Einträge in einer DataTable
 

3 Die Klassen DataRow-Collection und DataRow
3.1 Rowstate Eigenschaft (DataRow-Class)
       Beispiel 1: Eine Zeile über alle RowstateValues verändern
3.2 RowVersion Eigenschaft (DataRow-Class)
       Beispiel 1: Eigenschaft Rowversion auslesen und verändern
3.3  IsNull-Methode (DataRow.IsNull-Methode)
       Beispiel 1: die IsNull-Methode anwenden
3.4 Nach Datensätzen in einer Datentabelle suchen 
       Beispiel 1: Suchen nach dem PrimaryKey der Tabelle (Find-Methode der DataRowCollection-Klasse)
       Beispiel 2: Suchen nach beliebigen Feldern mit Platzhaltern (Select-Methode der DataTable-Klasse)
 

4 Methoden und Eigenschaften der Klasse DataColumnCollection und DataColumn
   Beispiel 1: Tabelle mit berechneten Spalten
4.1 Berechnete Spalten 
      Beispiel 1: Tabelle mit berechneten Spalten erstellen
4.2 DataTable-Eigenschaft: PrimaryKey
      Beispiel 1: PrimaryKey auf ein einziges Datenfeld setzen
      Beispiel 2: PrimaryKey auf mehrere Datenfeldern setzen

 

5 Arbeiten mit DataTables
5.1 DataTables kopieren und klonen 
      Beispiel 1: Kopieren und Klonen einer DataTable
5.2  Aus einer DataTable eine DataView erstellen 
       Beispiel 1: Aus einer DataTable eine DataView erzeugen - Filtern und Sortieren der View
       Beispiel 2: Eine Dataview in eine neue DataTable exportieren (ToTable-Methode der DataView-Klasse)
5.3 Tabellen miteinander in Beziehung setzen
       Beispiel 1: Zwei Tabellen mit der Relations-Klasse in Beziehung setzen
5.4 Tabellen mergen
       Beispiel 1: Zwei Tabellen kombinieren (mergen)
5.5 Zeilen von einer Tabelle in eine andere Tabelle kopieren
       Beispiel: Wer ist oder war mit meinem Rechner verbunden
5.6 DataSets und DataTables Serialisieren und Deserialisieren (=Export und Import nach und von XML-Dateien)

       Beispiel 1: Beispieldaten und Schema einer DatenTabelle in XML-Files exportieren und wieder importieren
5.7 DataTable-Objekte aus Funktionen zurückgeben
5.8 DataTables anstelle von Arrays und HashTabellen benutzen
      Beispiel 1: Eine DataTable ähnlich einem Array benutzen
      Beispiel 2: Mit der Find-Methode eine DataTable ähnlich einer Hashtabelle nutzen

 

1 Disconnected Classes (DataSet /DataTable)

MSDN: DataSets, DataTables und DataViews (ADO.NET)

Nach den verbundenen Klassen aus der ADO.Net Bibliothek komme ich nun zu den unverbundenen Klassen (=DatenKonsumenten), die die von den verbundenen Klassen (=DatenProvider) angelieferten Daten weiter verarbeiten.

Die zentrale Klasse der Disconnected Classes ist das DataSet, das aus DataTables und möglicherweise auch aus DataViews bestehen kann. Ein DataSet ist als eine eigenständige Datenbank im Arbeitsspeicher, bestehend aus einer oder mehreren Tabellen, Verknüpfungen und Sichten/ Queries, zu sehen.

Im folgenden Link könnt ihr das Objektmodell eines DataSets sehen. In dem Link findet ihr eine kurze Beschreibung der drei Collections "The DataTableCollection". "The DataRelationCollection" und "ExtendedProperties"

MSDN: ADO.NET DataSet

Ich werde zunächst in Kapitel 1.1 auf die Datentabellen genauer eingehen, um davon aus dann auf das DataSet als übergeordnete Klasse zu behandeln.

 

2 DataTable Klasse

Die DataTable ist der wichtigste Bestandteil eines Datasets, ähnlich wie eine Datenbanktabelle der wichtigste Bestandteil einer Datenbank ist.
Das DataTable-Objekt stellt eine, von einer Datenquelle unabhängige Tabelle im Memory des Rechners dar. Die Tabelle besteht naturgemäß aus Feldern (Spalten) mit Randbedingung und den Zeilen (Datensätzen). 

 

2.1 Erstellen von Tabellen (Übersicht)

DataTables können beim Skripten über .Net auf mehrere Arten erstellt werden.

  1. als von einem DataSet unabhängige Tabelle
  2. als neue (weitere) Tabelle in einer Tables-Collection (Teil eines DataSets)
  3. mit der Fill-Methode des DataAdapters
     

2.1.1  von einem DataSet unäbhängige Tabelle erstellen

In den nächsten Beispielen erstelle ich DataTables neu (=unabhängig) im Speicher, verzichte also auf eine Verbindung über die DataProviderKlassen zu einer Datenquelle wie Excel oder eine Datenbank.

Von DataSets unabhängige Tabellen können unter anderem als mächtige Alternativen zu Arrays oder Hashtabellen genutzt werden. Besonders wenn das Array oder der Hash mehrere Datenfelder (=Spalten) enthält, ist eine DataTable oft deutlich übersichtlicher

 

Beispiel 1a: Erstellen einer einfachen Tabelle mit einer Spalte und wenigen Datensätzen

Set-StrictMode -Version "2.0"
Clear-Host
 
#Anlage einer DataTabelle mit einer einzigen Spalte
$DataTable = New-Object System.Data.DataTable("MyFriends")
$Column1 = New-Object System.Data.DataColumn("FullName")
$DataTable.Columns.Add($Column1)

#Anhängen einer neuen Reihe
$Row1 = $DataTable.NewRow()
$Row1[$Column1] = "Karl Napf"
$DataTable.Rows.Add($Row1)

#einfacheres Anhängen von Datensätzen
$DataTable.Rows.Add("Micky Maus") | out-null
$DataTable.Rows.Add("Karl Knall") | out-null

#Ausgabe
$DataTable | Format-Table FullName

"TabellenName: {0}" -f $DataTable.Tablename
"Spaltenname: {0}" -f $DataTable.Columns[0].ColumnName
"Anzahl Datensätze: {0}" -f $DataTable.Rows.Count
#Ausgabe

FullName   
--------   
Karl Napf
Micky Maus
Karl Knall

TabellenName: MyFriends
Spaltenname: FullName
Anzahl Datensätze: 3

Die Spalte (=Feld) hat hier keine besonderen Eigenschaften erhalten. Im Kapitel x.y gehe ich genauer auf Spalteneigenschaften ein. Wenn euch das aber jetzt schon interessiert, könnt ihr euch das nächste etwas längere Beispiel anschauen.


Beispiel 1b: Erstellen einer Tabelle mit Spalten und im Skript definierten Datensätzen

Im folgenden Beispiel erstelle ich ein praxisnäheres DataTable-Objekt.

  • Objektname der DataTable: DataTableAuto
  • Name der Tabelle: MeineAutos
  • 4 Felder: Index, Fahrgestellenummer, Marke, Baujahr
  • 7 Datensätze für verschiedene Autos

Allerdings ist das Beispiel ein bischen länger, weil ein paar mehr Datensätze angelegt werden und weil ich Spalteneigenschaften wie AutoIncrement oder PrimaryKey verwende. Damit die Website nicht zu unübersichtlich wird, zeige ich das Beispiel nicht online, ihr könnt es aber downloaden. 

#Download: "Kap_1.1.1.1-Beispiel_1b.ps1.txt"

 Kap_1.1.1.1-Beispiel_1b.ps1.txt

Anmerkung:  

Das erste Feld (Index) dieser Tabelle ist als PrimaryKey mit Autowert definiert. Dies geschieht durch die Eigenschaften AutoIncrement* der DataColumn-Klasse

$Index.AutoIncrement = $True
$Index.AutoIncrementSeed = -1
$Index.AutoIncrementStep = -1
$DataTableAuto.PrimaryKey = $DataTableAuto.Columns["Index"]

Die Eigenschaften AutoIncrementedSeed und AutoIncrementedStep müssen besonders beachtet werden, wenn ein Austausch oder ein Zurückschreiben veränderter Datensätze in eine Datenbankapplikation stattfindet.
Bitte lest euch den Artikel "Order is important - Step then Seed" unter MSDN: DataColumn.AutoIncrementSeed Property (ganz unten) durch. 

 

Beispiel 2a: Ausgabe von Get-Process in DataTable einlesen

Hier erstelle ich eine DataTable, definiere ein paar Spalten und befülle diese Tabelle mit Systemdaten, die ich mir ganz einfach vom Cmdlet "Get-Process" besorge

Set-StrictMode -Version "2.0"
Clear-Host

#Definition der DataTable
$DataTable=New-Object System.Data.DataTable("Processes")

#Definition der Spalten
$Column0 = New-Object System.Data.DataColumn("ID")
$Column1 = New-Object System.Data.DataColumn("Handle")
$Column2 = New-Object System.Data.DataColumn("WorkingSet")
$Column3 = New-Object System.Data.DataColumn("ProcessName")
$DataTable.Columns.Add($Column0)
$DataTable.Columns.Add($Column1)
$DataTable.Columns.Add($Column2)
$DataTable.Columns.Add($Column3)

#Spezialfunktion der Spalte ["ID] zuweisen
$DataTable.PrimaryKey = $DataTable.Columns["ID"]

#DataTable füllen
$Processes = Get-Process
$Processes | ForEach{
  $DataTable.Rows.Add($($_.ID), $($_.Handle) ,$($_.WorkingSet),$($_.ProcessName)) | Out-null
  }
 
#Ausgabe einiger Tabelleneingenschaften
$DataTable | ft -auto

"Name der Tabelle: {0}" -f $DataTable.TableName
"Anzahl der Datensätze: {0}:" -f $DataTable.Rows.Count
"PrimaryKey: {0}:" -f $DataTable.PrimaryKey
""
"Der Datensatz #25:"
$DataTable.Rows[25] | Format-Table -AutoSize
#Ausgabe gekürzt

ID   Handle WorkingSet ProcessName          
--   ------ ---------- -----------          
3264 2392   6676480    AcroRd32             
...         
3828 2256   6172672    wmiprvse             


Name der Tabelle: Processes
Anzahl der Datensätze: 74:
PrimaryKey: ID:

Der Datensatz #25:

ID  Handle WorkingSet ProcessName
--  ------ ---------- -----------
272 2216   4194304    nsd       

 

Beispiel 2b: beliebige Ausgabe von cmdlets in eine DataTable umleiten

Set-StrictMode -Version "2.0"
Clear-Host

Function Main{
  $DataTableProperties = @{
    DataTableName = "Processes";
    DataTableColumns = @("ID","Handle","WorkingSet","ProcessName")
  }

  $Data = Get-Process | Select -First 5

  $DataTable = New-DataTable $DataTableProperties $Data
  $DataTable | ft  -auto
}#End MainFunction

Function New-DataTable{
   Param($DataTableProperties,$Data)
   $DataTableName = $DataTableProperties.DataTableName
   $DataTableColumns = $DataTableProperties.DataTableColumns

   #errorcheck
   ForEach($DataTableColumn in $DataTableColumns){
     Try{
       $Data.$DataTableColumn | Out-Null
     }Catch{
         Write-Host "DataTableColumn $DataTableColumn does not exist in your Data"
         Exit
     }
   }

   #creation of a datatable
   $myDataTable=New-Object System.Data.DataTable($DataTableName)
   ForEach($DataTableColumn in $DataTableColumns){
     $Column = New-Object System.Data.DataColumn($DataTableColumn)
     $myDataTable.Columns.Add($Column)
   }

   #fill the datatable with data
   $Data | ForEach{   
     $Row = $myDataTable.NewRow()
     Foreach($DataTableColumn in $DataTableColumns){
        $Row[$DataTableColumn] = $_.$DataTableColumn
     }
     $myDataTable.Rows.Add($Row)
    }
 
   Return ,$myDataTable
} #End New-Table

Main
#mögliche Ausgabe

ID   Handle WorkingSet ProcessName
--   ------ ---------- -----------
2960 1168   6848512    ACDaemon   
1432        3854336    ACService  
2924 2952   13471744   AdobeARM   
1456        3915776    armsvc     
2232        16637952   audiodg

Dies Beispiel ist allgemeiner als das vorige Beispiel gehalten. In der Function Main könnt ihr definieren, welche Spalten in der Datatable erscheinen sollen und welche Art von Daten überhaupt eingelesen werden sollen. Wenn ihr anstelle der Get-Process Daten lieber die Files aus einem Directory wollt, ersetzt die gelben Codezeilen einfach durch

.....
  DataTableName = "Files";
  DataTableColumns = @("Name","Fullname","LastWriteTime")
    }

  $Data = Get-ChildItem "C:\temp" | Select -First 5
.....

 

Beispiel 3: Einlesen einer CSV-Datei in eine DataTable

Für dieses und das nächste Beispiel könnt ihr euch mittels

Get-Process | Export-Csv -Path "C:\Temp\process.csv" -NoTypeInformation -Delimiter ","

schnell eine übliche CSV-Datei bauen

 

Beispiel 3a: Einlesen einer CSV-Datei in eine DataTable mit allen Spalten

In diesem Beispiel lade ich eine CSV-Dateien mit all ihren Spalten in eine DataTable ein.

Clear-Host
Set-StrictMode -Version "2.0"

Function Main{
   $Path = "c:\temp\process.csv"
   $Delimiter = ","
 
   #Funktionsaufruf
   $DataTable01 = New-CompleteDataTable -Path $Path -Delimiter $Delimiter
   

      #Beispielausgabe einiger Felder
   $DataTable01 | Select -First 5 | Format-Table Name,Company,CPU -auto
}#End Function Main

Function New-CompleteDataTable{
    <#
    .Synopsis liest eine komplette CSV-Datei in eine DataTable ein
    #>
    Param($Path)
   
    #region Definition der Datatable (=ColumnNames)
    $DataTable = New-Object System.Data.DataTable("xyz")
    $ColumnNamesString = (Get-Content -Path $Path)[0]
    $ColumnNames = $ColumnNamesString -Split $Delimiter
    ForEach($ColumnName in $ColumnNames){
      $ColumnName = $ColumnName.Replace('"','')
      $Column = New-Object System.Data.DataColumn($ColumnName)
      $DataTable.Columns.Add($Column)
    }
    #endregion Definition der Datatable (=ColumnNames)
   
   #region DataTable mit Daten aus dem Csv-File befüllen
   $CsvData = Import-Csv -Path $Path -delimiter $Delimiter
   ForEach($Data in $CsvData){
     $Row = $DataTable.NewRow()
     ForEach($ColumnName in $ColumnNames){
       $ColumnName = $ColumnName.Replace('"','')
       $Row[$ColumnName] = $Data.$ColumnName
     }
     #endregion DataTable mit Daten aus dem Csv-File befüllen
     $DataTable.Rows.Add($Row)
   }
   #endregion

   Return ,$DataTable
}#Ende
Function New-CompleteDataTable
 
#Funktionsaufruf der Hauptfunktion
Main
#mögliche Ausgabe

Name          Company               CPU      
----          -------               ---      
audiodg                                      
BtnHnd        FUJITSU LIMITED       0,0312002
BTStackServer Broadcom Corporation. 0,312002
BTTray        Broadcom Corporation. 0,2028013

Die Funktion "Main" benutze ich wieder nur, damit ich den Aufruf der eigentlich wichtigen Funktion "New-CompleteDataTable" am Anfang des Skripts durchführen kann.

In der eigentlich interessanten Funktion "New-CompleteDataTable" lese ich mit Get-Content die erste Zeile der Csv-Datei mit den Spalterüberschriften ein und füge die darin enthaltenen Spalten (=$ColumnNames) der vorher erstellten DataTable hinzu. Ich habe auch Beispiele im Netz gefunden, die diese Spaltenanlage aus dem "Import-Csv" - cmdlet ableiten, über Get-Content fand ich es aber verständlicher.

Im zweiten Schritt erstelle ich für jeden Datensatz eine neue Reihe ($DataTable.NewRow) und befülle jede Zelle mit dem entsprechenden Wert ($Data.ColumName).

Das Ersetzen der Anführungszeichen (Replace('"','')) durch Leerzeichen muss nicht unbedingt sein oder nicht jede Csv-Source wird überhaupt Anführungszeichen besitzen. Im konkreten Beispiel gefällts mir die Ausgabe ohne ".." einfach besser.

 

Beispiel 3b: Einlesen einer CSV-Datei in eine DataTable mit ausgewählten Spalten

Benötigt man von vornherein nicht alle Spalten der Csv-Quelle in der Datatable im Powershell, kann man den Import nur auf die benötigten Felder beschränken. Im nächsten Skript könnt ihr dir gewünschten Felder in das Array "Columns" in der Main-Function eintragen. Das Ergebnis ist identisch zum letzten Beispiel 3a

Clear-Host
Set-StrictMode -Version "2.0"

Function Main{
   $Path = "c:\temp\process.csv"
   $Delimiter = ","
   $Columns = @("Name","Company","CPU","WS")
 
   #Funktionsaufruf
   $DataTable02 = New-SelectedDataTable -Path $Path -Delimiter $Delimiter -ColumnNames $Columns
 
   #Beispielausgabe
   $myFormat = "Name","Company",@{Label = "WS"; Expression = {"{0:0.00}" -f $($_.WS/1mb)}}
      $DataTable02 | Select -First 3 | Format-Table $myFormat -AutoSize 

 }#End Function Main

Function New-SelectedDataTable{
    <#
    .Synopsis liest ausgewählte Spalten aus einer CSV-Datei in eine DataTable ein
    #>
    Param($Path, $ColumnNames)

    #region Definition der Datatable (=ColumnNames)
    $DataTable = New-Object System.Data.DataTable("xyz")
    ForEach($ColumnName in $ColumnNames){
      $Column = New-Object System.Data.DataColumn($ColumnName)
      $DataTable.Columns.Add($Column)
    }
    #endregion Definition der Datatable (=ColumnNames)

    #region DataTable mit Daten aus dem Csv-File befüllen
    $CsvData = Import-Csv -Path $Path -delimiter $Delimiter
    ForEach($Data in $CsvData){
     $Row = $DataTable.NewRow()
     ForEach($ColumnName in $ColumnNames){
       $Row[$ColumnName] = $Data.$ColumnName
     }
     #endregion DataTable mit Daten aus dem Csv-File befüllen
     $DataTable.Rows.Add($Row)
   }
   Return ,$DataTable
}#end
Function New-SelectedDataTable 
#Funktionsaufruf der Hauptfunktion
Main

Der Unterschied zu Beispiel 3a besteht darin, dass ich die SpaltenNamen (=ColumnNames) in der Funktion New-SelectedDataTable nicht mehr aus dem csv-File exportiere, sondern in der Funktion Main im Array Columns definiere. Bei großen Tabellen mit vielen Datensätzen und Datenfeldern spart man so natürlich Speicherplatz und gewinnt an Performance.

Wie man Tabellen bei der Ausgabe formatieren kann, könnt ihr euch unter "Formatierung des Outputs -> Formatierte Tabellen" ansehen.

 

2.1.2 neue (weitere) Tabellen in einer DataTableCollection erstellen

Wenn ihr euch den Objektbaum zu Beginn des Kapitels 2 anseht, so steht dort die DataTable-Klasse unterhalb der Klasse DataTableCollection. Und wenn ihr euch weiter in der MSDN die zugehörige Klasse DataTableCollection Class anseht, werdet ihr feststellen, dass diese Klasse keine (öffentlichen) Konstruktoren besitzt, also keine eigene Instanz dieser Collection-Klasse erstellt werden kann. 
Es ist daher nur möglich ein DataTableCollection-Objekt unterhalb eines DataSets zu erstellen. Ein unabhängiges CollectionObject ist nicht möglich.

 

Beispiel 1: Erzeugen einer DataTableCollection

Set-StrictMode -Version "2.0"
Clear-Host

#Definition des DataSets
$DataSet = New-Object System.Data.DataSet

#Hinzufügen von DataTables zur DataTableCollection
#$DataTable1 = New-Object System.Data.DataTable
$DataTable1 = $DataSet.Tables.Add("Beispiel1")
$DataTable2 = $DataSet.Tables.Add("Beispiel2")

#Ausgabe
"DatenTyp von DataTable1: {0}" -f $DataTable1.Gettype().Name
"DatenTyp von DataSet.Tables: {0}" -f $DataSet.Tables.Gettype().Name
"Anzahl Tabellen im DataSet: {0}" -f $DataSet.Tables.Count
"Tabellenname von DataTable1: {0}" -f $DataTable1.TableName
#Ausgabe

DatenTyp von DataTable1: DataTable
DatenTyp von DataSet.Tables: DataTableCollection
Anzahl Tabellen im DataSet: 2
Tabellenname von DataTable1: Beispiel1

 

2.2 Fill-Methode des DataAdapters

MSDN: DataAdapter.Fill-Methode

seht euch dazu bitte das Kapitel 1.4.1.1 Fill-Methode an. Dort sind bereits einige Beispiele, wie Daten aus einer Datenquelle in einen DataAdapter geholt und von dort weiter in eine DataTable geschrieben werden können. 

...
$DataAdapter.Fill($DataTable)
...

 

2.3 Direkter Zugriff auf Einträge in einer DataTable

In diesem Beispiel zeige ich verschiedene Möglichkeiten, wie man in einer Datatable auf bestimmte einzelne Zellen, ganze Zeilen und ganze Spalten zugreifen kann

Set-StrictMode -Version "2.0"
Clear-Host
 
#Anlage einer DataTabelle mit einer einzigen Spalte
$DataTable = New-Object System.Data.DataTable("MyFriends")
$ColumnVorname = New-Object System.Data.DataColumn("Vorname")
$ColumnNachname = New-Object System.Data.DataColumn("Nachname")
$DataTable.Columns.Add($ColumnVorname)
$DataTable.Columns.Add($ColumnNachname)
 
#Anhängen von Datensätzen
$DataTable.Rows.Add("Micky","Maus") | out-null
$DataTable.Rows.Add("Donald","Duck") | out-null #DataRow mit Nullwert
$DataTable.Rows.Add("Oma","Duck") | out-null
$DataTable.AcceptChanges()

#die ganze Tabelle zum Überblick
$DataTable | Format-Table Vorname, Nachname -auto
"-" * 40 + "`n"

#Ab hier Zugriff auf die Felder

#Zugriff auf eine einzelne, ganze Zeile
$String = "`n1. Zugriff auf eine einzelne Zeile"
Write-Host $String -BackgroundColor DarkRed -ForegroundColor DarkYellow
$DataTable.Rows[0] | Foreach {
  "{0} {1}" -f $_.Vorname,$_.Nachname
   }

#$DataTable.Rows[0] | Format-Table -HideTableHeaders

#Zugriff auf mehrere Zeilen
$String = "`n2. Zugriff auf mehrere Zeilen"
Write-Host $String -BackgroundColor DarkRed -ForegroundColor DarkYellow
$DataTable.Rows[1,2]  | Foreach {
  "{0} {1}" -f $_.Vorname,$_.Nachname
   }
#$DataTable.Rows[1,2] | Format-Table -HideTableHeaders 
  
#Zugriff auf eine oder mehrere Zellen einer Zeile
$String = "`n3. Zugriff auf eine oder mehrere Zellen einer Zeile"
Write-Host $String  -BackgroundColor DarkRed -ForegroundColor DarkYellow
#$DataTable.Rows[0,2].$ColumnVorname
$DataTable.Rows[0,2].Vorname

#Zugriff auf eine Zelle
$String = "`n4. Zugriff auf eine Zelle"
Write-Host $String -BackgroundColor DarkRed -ForegroundColor DarkYellow
$DataTable.Rows[1][1]

#Zugriff auf eine ganze Spalte
$String = "`n5. Zugriff auf eine ganze Spalte"
Write-Host $String -BackgroundColor DarkRed -ForegroundColor DarkYellow
$DataTable.Rows.Vorname
#$DataTable.Rows.$ColumnVorname

#Zugriff auf mehrere Zellen ganze Spalte
$String = "`n6. Zugriff auf mehrere Zellen aus einer Spalte"
Write-Host $String -BackgroundColor DarkRed -ForegroundColor DarkYellow
$DataTable.Rows.Vorname[1,2]
#Ausgabe

Vorname Nachname
------- --------
Micky   Maus    
Donald  Duck    
Oma     Duck    

----------------------------------------

1. Zugriff auf eine einzelne Zeile
Micky Maus

2. Zugriff auf mehrere Zeilen
Donald Duck
Oma Duck

3. Zugriff auf eine oder mehrere Zellen einer Zeile
Micky
Oma

4. Zugriff auf eine Zelle
Duck

5. Zugriff auf eine ganze Spalte
Micky
Donald
Oma

6. Zugriff auf mehrere Zellen aus einer Spalte
Donald
Oma

 

3 DataRow-Collection und DataRow-Class

Zur Orientierung könnt ihr Im Objektbaum zu Beginn von Kaptel 2 nach diesen beiden Klassen suchen. Im Detail geht es in diesem Kapitel um Eigeschaften und Methoden von Rows (=Zeilen). Es gibt Eigenschaften, anhand derer der Code erkennen kann, ob eine Zeile unverändert vorliegt, oder ob bereits verschiedene Aktionen an ihr vorgenommen wurden (RowState/ RowVersion). 
Mit der find-Methode können dagegen Zeilen gefunden werden, deren PrimaryKey einen bestimmten Wert enthält.

 

3.1 Rowstate Eigenschaft (DataRow-Class)

MSDN: DataRowState Enumeration

Diese 5 Werte bekommt man auch nativ aus der Powershell

[Enum]::GetValues([System.Data.DataRowState])

 

Beispiel 1: Eine Zeile über alle RowstateValues verändern    

Set-StrictMode -Version "2.0"
Clear-Host

 
#Anlage einer Tabelle mit einer einzigen Spalte
$DataTable=New-Object System.Data.DataTable("TestTable")
$Column1 = New-Object System.Data.DataColumn("Column1")
$DataTable.Columns.Add($Column1)

"TabellenName: {0}" -f $DataTable.Tablename
"Spaltenname: {0}" -f $DataTable.Columns[0].ColumnName

 
#Verändern der Eigenschaft Rowstate
 
"`n"
#-------Teil A  -> Detached
"-" * 10 + "Teil A" + "-" * 10 + "`n"

$Row1=$DataTable.NewRow()
$Row1["Column1"]="4711"

"Anzahl Zeilen: {0}" -f $DataTable.Rows.Count
"Status von Row1: {0}" -f $Row1.Rowstate

"-" * 25
#--------

#------Teil B  -> Added
"-" * 10 + "Teil B" + "-" * 10 + "`n"

$DataTable.Rows.Add($Row1)
"Anzahl Zeilen: {0}" -f $DataTable.Rows.Count
"Status von Row1: {0}" -f $Row1.Rowstate

"-" * 25
#------------

#-------Teil C -> Unchanged
"-" * 10 + "Teil C" + "-" * 10 + "`n"
$($DataTable.Rows[0]).AcceptChanges()
#$DataTable.AcceptChanges() #für ganze Datatable
"Status von Row1: {0}" -f $Row1.Rowstate

"-" * 25
#----------

#-------Teil D -> Modified
"-" * 10 + "Teil D" + "-" * 10 + "`n"

$Row1["Column1"]="4712"
"Status von Row1: {0}" -f $Row1.Rowstate

"-" * 25
#--------

#-------Teil E -> Deleted
"-" * 10 + "Teil D" + "-" * 10 + "`n"

#$Row1.Delete()
$DataTable.Rows[0].Delete()
"Status von Row1: {0}" -f $Row1.Rowstate
"Anzahl Zeilen: {0}" -f $DataTable.Rows.Count

"-" * 25
#--------

#-------Teil F -> Detached
"-" * 10 + "Teil F" + "-" * 10 + "`n"
$($DataTable.Rows[0]).AcceptChanges()

"Status von Row1: {0}" -f $Row1.Rowstate
"Anzahl Zeilen: {0}" -f $DataTable.Rows.Count

"-" * 25
#--------

#$DataTable.Getchanges()

TabellenName: TestTable
Spaltenname: Column1

----------Teil A----------
Anzahl Zeilen: 0
Status von Row1: Detached
-------------------------

----------Teil B----------
Anzahl Zeilen: 1
Status von Row1: Added
-------------------------

----------Teil C----------
Status von Row1: Unchanged
-------------------------

----------Teil D----------
Status von Row1: Modified
-------------------------

----------Teil E----------
Status von Row1: Deleted
Anzahl Zeilen: 1
-------------------------

----------Teil F----------
Status von Row1: Detached
Anzahl Zeilen: 0
-------------------------

Wie man anhand der MSDN-Tabelle und an diesem Beispiel sieht, kann ein Datensatz 5 verschiedene Zustände (RowState) annehmen: "Detached", "Added", "Unchanged", "Modified" und "Deleted" annehmen. Nach diesen Zuständen kann man auch jederzeit Datensätze filtern.

 

3.2 RowVersion Eigenschaft (DataRow-Class)

MSDN: DataRowVersion Enumeration

Eine DataTable-Object kann bis zu 3 Versionen jeder Datenreihe enthalten (Original, Current und Default). Im Beispiel 1 unten, Teil A, sieht man, dass nach dem Erstellen einer Datatable zuerst nur die Version "Current" existiert. Die "Original"-Version kommt erst hinzu, wenn sich die Datenreihe verändert hat (siehe Teil C in Beispiel 1), was vielleicht etwas ungewöhnlich erscheinen mag.

[Enum]::GetValues([System.Data.DataRowVersion])

 

Beispiel 1: Eigenschaft Rowversion auslesen und verändern

Set-StrictMode -Version "2.0"
Clear-Host

#Anlage einer Tabelle mit einer einzigen Spalte/ einzigem Datensatz
$DataTable=New-Object System.Data.DataTable("TestTable")
$Column1 = New-Object System.Data.DataColumn("Column1")
$DataTable.Columns.Add($Column1)
$Row1 = $DataTable.NewRow()
$Row1[$Column1] = "Karl Napf"
$DataTable.Rows.Add($Row1)

#Verändern der Eigenschaft RowVersion
 
#------Teil A
"`Teil A)" + "-" * 35
"Orginal existiert: {0}" -f $Row1.HasVersion("Original")
"Current existiert: {0}" -f $Row1.HasVersion("Current")
"Proposed existiert: {0}" -f $Row1.HasVersion("Proposed")
#$Row1.hasversion([System.Data.DataRowVersion]("Current"))

 
 ""
"Orginal:{0}" -f  $Row1[0,"Original"]
"Current:{0}" -f  $Row1[0,"Current"]
"Current:{0}" -f  $Row1[0,"Proposed"]
 
#------Teil B
"`nTeil B)" + "-" * 35
#BeginEdit/ EndEdit-Methoden

$Row1.BeginEdit()
$Row1[0]="Kater Karlo"

"Orginal:{0}" -f  $Row1[0,"Original"]
"Current:{0}" -f  $Row1[0,"Current"]
"Proposed:{0}" -f  $Row1[0,"Proposed"]
$Row1.EndEdit()

#------Teil C
"`nTeil C)" + "-" * 35

#Änderungen akzeptieren, ablehnen
$Row1.Acceptchanges()
#$DataTable.AcceptChanges()
#$DataTable.RejectChanges()

"Orginal:{0}" -f  $Row1[0,"Original"]
"Current:{0}" -f  $Row1[0,"Current"]
"Proposed:{0}" -f  $Row1[0,"Proposed"]

#------Teil D
"`nTeil D)" + "-" * 35

#Datenreihe löschen
$Row1.Delete()

"Orginal:{0}" -f  $Row1[0,"Original"]
"Current:{0}" -f  $Row1[0,"Current"]
"Proposed:{0}" -f  $Row1[0,"Proposed"]
#$Row1.Acceptchanges()

#Ausgabe

Teil A)-----------------------------------
Orginal existiert: False
Current existiert: True
Proposed existiert: False

Orginal:
Current:Karl Napf
Current:

Teil B)-----------------------------------
Orginal:
Current:Karl Napf
Proposed:Kater Karlo

Teil C)-----------------------------------
Orginal:Kater Karlo
Current:Kater Karlo
Proposed:

Teil D)-----------------------------------
Orginal:Kater Karlo
Current:
Proposed:

  • Wie man sieht, beeinflussen die Methoden "BeginEdit", "EndEdit", "CancelEdit", "AcceptChanges" und "CancelChanges" die Rowversion-Eigenschaft. Besonders die "CancelChanges" finde ich ganz interessant, weil damit sämtliche Veränderungen an einem DataSet bei wieder verworfen werden können. 
     
  • Im Eingangs geannten MSDN-Artikel findet ihr unter "Remarks" in Prosa erklärt, wann eine DataRow welchen Zustand annimmt. 
     
  • Im Beispiel sieht man auch, das "AcceptChanges" und "CancelChanges" sowohl auf das ganze DataTable-Objekt, wie auch auf eine einzelne Zeile angewendet werden können

$Row1.Acceptchanges()
$DataTable.AcceptChanges()
$DataTable.RejectChanges()

 

3.3 IsNull-Methode (DataRow.IsNull-Methode)

MSDN: DataRow.IsNull-Methode

Die IsNull-Methode kann nach diesem Link auf verschiedene Arten genutzt werden:

 

Beispiel 1: die IsNull-Methode anwenden

Set-StrictMode -Version "2.0"
Clear-Host
 
#Anlage einer DataTabelle mit einer einzigen Spalte
$DataTable=New-Object System.Data.DataTable("MyFriends")
$ColumnVorname = New-Object System.Data.DataColumn("Vorname")
$ColumnNachname = New-Object System.Data.DataColumn("Nachname")
$DataTable.Columns.Add($ColumnVorname)
$DataTable.Columns.Add($ColumnNachname)
 
#Anhängen von Datensätzen
$DataTable.Rows.Add("Micky","Maus") | out-null
Set-StrictMode -Version "2.0"
Clear-Host

$DataTable.Rows.Add("Donald",$Null) | out-null #DataRow mit Nullwert
$DataTable.Rows.Add("Oma","Duck") | out-null
$DataTable.AcceptChanges()

#die ganze Tabelle zum Überblick
$DataTable | Format-Table Vorname, Nachname -auto
"-" * 40 + "`n"

#Ab hier Anwenden der IsNull-Methode

#1. IsNull(DataColumn)
Write-Host "1. IsNull(DataColumn)" -BackgroundColor DarkRed -ForegroundColor DarkYellow
$DataTable.Rows.Isnull($ColumnNachname)
"--"
$DataTable.Rows[1].Isnull($ColumnNachname)

#2. IsNull(Int32)
Write-Host "`n2. IsNull(Int32)" -BackgroundColor DarkRed -ForegroundColor DarkYellow
$DataTable.Rows.Isnull(1)
"--"
$DataTable.Rows[1].Isnull(1)


#3. IsNull(String)
Write-Host "`n3. IsNull(String)" -BackgroundColor DarkRed -ForegroundColor DarkYellow
$DataTable.Rows.Isnull("Nachname")
"--"
$DataTable.Rows[1].Isnull("Nachname")

#4. IsNull(DataColumn,DataRowVersion)
Write-Host "`n4. IsNull(DataColumn,DataRowVersion)" -BackgroundColor DarkRed -ForegroundColor DarkYellow
$RowVersion=[System.Data.DataRowVersion]::Original #vrl. Kapitel 1.1.2.2 DataRowVersion
#$DataTable.Rows.Isnull("Nachname",$RowVersion) #geht nicht
$DataTable.Rows.Isnull($ColumnNachname,$RowVersion)
#Ausgabe
 
Vorname Nachname
------- --------
Micky   Maus    
Donald          
Oma     Duck    

----------------------------------------

1. IsNull(DataColumn)
False
True
False
--
True

2. IsNull(Int32)
False
True
False
--
True

3. IsNull(String)
False
True
False
--
True

4. IsNull(DataColumn,DataRowVersion)
False
True

False

Man kann also immer in einer Spalte nach Nullwerten suchen. Jenachdem ob man eine Zeilennummer mitgibt oder nicht, wird die gesamte Spalte durchsucht oder nicht.

 

3.4 Nach Datensätzen in einer Datentabelle suchen

In diesem Artikel seht ihr, dass es für diese Aufgabenstellung mehrere Möglichkeiten gibt, von denen ich ein paar zeigen will

 

Beispiel 1: Suchen nach einem einspaltigen PrimaryKey der Tabelle (Find-Methode der DataRowCollection-Klasse)

Die PrimeraryKey-Eigenschaft erkläre ich etwas weiter unten in Kapitel 1.1.4.2

Set-StrictMode -Version "2.0"
Clear-Host
 
#Anlage einer DataTabelle mit einer einzigen Spalte
$DataTable=New-Object System.Data.DataTable("MyFriends")
$ColumnVorname = New-Object System.Data.DataColumn("Vorname")
$ColumnNachname = New-Object System.Data.DataColumn("Nachname")
$DataTable.Columns.Add($ColumnVorname)
$DataTable.Columns.Add($ColumnNachname)
 
#Anhängen von Datensätzen
$DataTable.Rows.Add("Micky","Maus") | Out-Null
$DataTable.Rows.Add("Donald",$Null) | Out-Null #DataRow mit Nullwert
$DataTable.Rows.Add("Oma","Duck") | Out-Null
$DataTable.AcceptChanges()

#die ganze Tabelle zum Überblick
#$DataTable | Format-Table Vorname, Nachname -auto
#"-" * 40 + "`n"


#Die Find Methode benötigt zum Suchen einen Primarykey in der Tabelle

#Einspaltigen PrimaryKey setzen
$DataTable.PrimaryKey = $DataTable.Columns["Vorname"]

 

#Verschiedene Arten auf die Felder eines gefundenen Datensatzes zuzugreifen

Try {
  $DataTable.Rows.Find("Micky").Nachname
  $DataTable.Rows.Find("Micky")["Nachname"]
  $DataTable.Rows.Find("Micky").$Columnnachname
  $DataTable.Rows.Find("Micky")[1]
  $DataTable.Rows.Find("Micky") | Format-Table -HideTableHeaders

}Catch{

  "Es konnte keine Übereinstimmung gefunden werden"

}

#Ausgabe

Maus
Maus
Maus
Maus

Micky   Maus  

 

Beispiel 2: Suchen nach einem mehrspaltigen PrimaryKey

Set-StrictMode -Version "2.0"
Clear-Host
 
#Anlage einer DataTabelle mit einer einzigen Spalte
$DataTable=New-Object System.Data.DataTable("MyFriends")
$ColumnVorname = New-Object System.Data.DataColumn("Vorname")
$ColumnNachname = New-Object System.Data.DataColumn("Nachname")
$ColumnBeruf = New-Object System.Data.DataColumn("Beruf")
 
$DataTable.Columns.Add($ColumnVorname)
$DataTable.Columns.Add($ColumnNachname)
$DataTable.Columns.Add($ColumnBeruf)
 
#Anhängen von Datensätzen
$DataTable.Rows.Add("Micky","Maus","Detektiv") | Out-Null
$DataTable.Rows.Add("Donald","Duck","Onkel") | Out-Null #DataRow mit Nullwert
$DataTable.Rows.Add("Oma","Duck","Kuchenbäckerin") | Out-Null
$DataTable.AcceptChanges()
 
#die ganze Tabelle zum Überblick
#$DataTable | Format-Table Vorname, Nachname -auto
#"-" * 40 + "`n"
 
#Mehrspaltigen PrimaryKey setzen
$DataTable.PrimaryKey = $DataTable.Columns["Vorname","Nachname"]
 
#Alternative
#$PrimaryKeys = @()
#$PrimaryKeys += $DataTable.Columns.Item("Vorname")
#$PrimaryKeys += $DataTable.Columns.Item("Nachname")
#$DataTable.PrimaryKey = $PrimaryKeys
 
Try {
  $DataTable.Rows.Find(@("Micky","Maus")) | Format-Table 
}Catch{
  "Es konnte keine Übereinstimmung gefunden werden"
}

#Ausgabe

 

Vorname Nachname Beruf   
------- -------- -----   
Micky   Maus     Detektiv

 

Beispiel 3: Suchen nach beliebigen Feldern mit Platzhaltern (Select-Methode der DataTable-Klase)

MSDN: DataTable.Select Methode

Set-StrictMode -Version "2.0"
Clear-Host
 
#Anlage einer DataTabelle mit einer einzigen Spalte
$DataTable=New-Object System.Data.DataTable("MyFriends")
$ColumnVorname = New-Object System.Data.DataColumn("Vorname")
$ColumnNachname = New-Object System.Data.DataColumn("Nachname")
$ColumnAlter = New-Object System.Data.DataColumn("Alter")
$DataTable.Columns.Add($ColumnVorname)
$DataTable.Columns.Add($ColumnNachname)
$DataTable.Columns.Add($ColumnAlter)
 
#Anhängen von Datensätzen
$DataTable.Rows.Add("Micky","Maus","37") | Out-Null
$DataTable.Rows.Add("Donald","Duck","39") | Out-Null #DataRow mit Nullwert
$DataTable.Rows.Add("Oma","Duck","87") | Out-Null
$DataTable.AcceptChanges()
 
#die ganze Tabelle zum Überblick
#$DataTable | Format-Table Vorname, Nachname -auto
#"-" * 40 + "`n"
 
#PrimaryKey setzen (ist sinnvoll, aber bei Select nicht notwendig)
$DataTable.PrimaryKey = $DataTable.Columns[0]
 
$SuchFilter = "'Du%'"
$DataTable.Select("Nachname like 'Du%'") | Format-Table
$DataTable.Select("Nachname like $Suchfilter") | Format-Table

#Ausgabe

 

Vorname Nachname Alter
------- -------- -----
Donald  Duck     39   
Oma     Duck     87   
 
Vorname Nachname Alter
------- -------- -----
Donald  Duck     39   
Oma     Duck     87   

Der Unterschied zum vorigen Beispiel besteht nur in den letzten drei Zeilen

 

4 Methoden und Eigenschaften der Klasse DataColumnCollection- und DataColumn

MSDN: DataColumn Class

MSDN: DataColumnCollection Class

 

4.1 Berechnete Spalten (DataColumn.Expression Property)

MSDN: DataColumn.Expression Property

 

Beispiel 1: Tabelle mit berechneten Spalten erstellen

Set-StrictMode -Version "2.0"
Clear-Host
 
#Anlage einer DataTabelle mit einer einzigen Spalte
$DataTable=New-Object System.Data.DataTable("MyFriends")
$Vorname = New-Object System.Data.DataColumn("Vorname")
$Nachname = New-Object System.Data.DataColumn("Nachname")
$DataTable.Columns.Add($Vorname)
$DataTable.Columns.Add($Nachname)
 
#Berechnete Spalte einfügen
$ColumnFullname = New-Object System.Data.DataColumn("FullName")
$DataTable.Columns.Add($ColumnFullname)
$ColumnFullname.Expression = "$Vorname + ' ' + $Nachname" 
 
#Anhängen von Datensätzen
$DataTable.Rows.Add("Micky","Maus") | out-null
$DataTable.Rows.Add("Karl","Napf") | out-null
 
#Ausgabeformen
$DataTable | Format-Table Vorname, Nachname, Fullname -auto
$DataTable | Out-GridView
$DataTable | Export-Csv "c:\temp\test.txt"
#Ausgabe
 
Vorname Nachname FullName  
------- -------- -------- 
Micky   Maus     Micky Maus
Karl    Napf     Karl Napf

Über die Eigenschaft "Expression" der Column-Klasse  können weitere Spalten hinzugefügt werden, die aus anderen Spalten berechnet oder zusammengesetzt werden. 

Die Ausgabe über das cmdlet Out-GridView liefert diesen Screen

Auch die Ausgabe in eine Csv-Datei mit dem cmdlet Export-Csv ist sicher gelegentlich brauchbar.

 
Nachdem im vergangenen Kapitel allgemein das Erstellen von DataTables gezeigt wurde, gehe ich jetzt genauer auf die die DataRow- und DataColumn-Klassen ein, ohne die eine Tabelle nicht vorstellbar wäre.

 

4.2 DataTable-Eigenschaft: PrimaryKey

Wie Tabellen in jeder Datenbank können auch Tabellen eines Datasets eine Spalte mit der Eigenschaft "PrimaryKey" besitzen. Im Normalfall besteht ein Primarykey aus einem eindeutigen Merkmal wie Kundennummer oder Artikelnummer.

 

Beispiel 1: PrimaryKey auf ein einziges Datenfeld setzen

Set-StrictMode -Version "2.0"
Clear-Host
 
#Anlage einer DataTabelle mit einer einzigen Spalte
$DataTable=New-Object System.Data.DataTable("MyFriends")
$ColumnPersonalNummer = New-Object System.Data.DataColumn("PersoNr")
$ColumnVorname = New-Object System.Data.DataColumn("Vorname")
$ColumnNachname = New-Object System.Data.DataColumn("Nachname")
$DataTable.Columns.Add($ColumnPersonalNummer)
$DataTable.Columns.Add($ColumnVorname)
$DataTable.Columns.Add($ColumnNachname)
 
#Anhängen von Datensätzen
$DataTable.Rows.Add(1001,"Micky","Maus") | out-null
$DataTable.Rows.Add(1002,"Donald","Duck") | out-null
$DataTable.Rows.Add(1007,"Oma","Duck") | out-null
$DataTable.AcceptChanges()

#die ganze Tabelle zum Überblick
$DataTable | Format-Table -auto -HideTableHeaders
"-" * 40 + "`n"

#Einer Spalte die Eigenschaft PrimaryKey zuweisen (vgl. Beispiel 2)
$DataTable.PrimaryKey = $DataTable.Columns[$ColumnPersonalNummer]
#$DataTable.PrimaryKey = $DataTable.Columns[0]

"PrimaryKey ist die Spalte: $($DataTable.PrimaryKey)`n"
#Ausgabe

1001    Micky   Maus    
1002    Donald  Duck    
1007    Oma     Duck    

----------------------------------------
PrimaryKey ist die Spalte: PersoNr

MSDN: DataTable.PrimaryKey Property

 

Beispiel 2: PrimaryKey auf mehrere Datenfeldern setzen

Natürlich können der Primarykey sich auch aus mehreren Spalten zusammensetzen. 

#...siehe Beispiel 1 oben

#Mehreren Spalten die Eigenschaft PrimaryKey zuweisen
$DataTable.PrimaryKey = $DataTable.Columns[1,2]
 
$PrimaryKeys = @()
$PrimaryKeys += $DataTable.Columns.Item("Vorname")
$PrimaryKeys += $DataTable.Columns.Item("Nachname")
$DataTable.PrimaryKey = $PrimaryKeys
 
"PrimaryKey ist die Spalte: $($DataTable.PrimaryKey)`n"

#Ausgabe

 

1001    Micky   Maus    
1002    Donald  Duck    
1007    Oma     Duck    
 
----------------------------------------
PrimaryKey ist die Spalte: Vorname Nachname

 

5 Arbeiten mit DataSets und DataTables

5.1 DataTables kopieren und klonen

 

Beispiel 1: Kopieren und Klonen einer Datatable

Beim Kopieren wird ein DataTable-Object mit Schema und Daten dupliziert, beim Klonen nur das Schema ohne Daten

Set-StrictMode -Version "2.0"
Clear-Host
 
Function Main{
  #create the schema of a datatable
  $myDataTableCar = New-DataTable
  
  #add some data
  $myDataTableCar = Fill-DataTable $myDataTableCar
  
  Write-Host "`original table" -BackGroundColor Black
  $myDataTableCar | Format-Table -AutoSize
    
  #Copy
  Write-Host "`copy table" -BackGroundColor Black
  $yourDataTableCar = New-Object System.Data.DataTable("YourCars")
  $yourDataTableCar = $myDataTableCar.Copy()
  $yourDataTableCar | Format-Table -AutoSize
 
  #Clone()
  Write-Host "`clone table with one imported row" -BackGroundColor Black
  $ClonedDataTableCar = New-Object System.Data.DataTable("ClonedCars")
  $ClonedDataTableCar = $myDataTableCar.Clone()
  $ClonedDataTableCar.ImportRow($myDataTableCar.Rows[3])
  $ClonedDataTableCar | Format-Table -AutoSize
} #end of main
 
Function New-DataTable {
 $DataTableCar = New-Object System.Data.DataTable("MyCars")
 
 #creating datafields (=columns)
 $Index = New-Object System.Data.DataColumn("Index")
 $Index.AutoIncrement = $True
 $Index.AutoIncrementSeed = 10
 $Index.AutoIncrementStep = 2
 $DataTableCar.Columns.Add($Index)
 $DataTableCar.PrimaryKey = $DataTableCar.Columns["Index"]
 
 $ChassisNumber = New-Object System.Data.DataColumn("ChassisNumber")
 $ChassisNumber.MaxLength = 25
 $ChassisNumber.Unique = $True
 $ChassisNumber.AllowDBNull = $False
 $ChassisNumber.Caption = "ChassisNumber"
 $DataTableCar.Columns.Add($ChassisNumber)
 
 $Brand = New-Object System.Data.DataColumn("Brand")
 $Brand.MaxLength = 23
 $DataTableCar.Columns.Add($Brand)
 
 $BuildYear = New-Object System.Data.DataColumn("BuildYear","Int32")
 $BuildYear.AllowDBNull= $False
 $DataTableCar.Columns.Add($BuildYear)
 
 Return ,$DataTableCar
 #Return "",$DataTableCar
} #end of function
 
Function Fill-DataTable{
  #Add Data (DataRow Objects)
 
  Param($DataTableCar)
 
  $NewCar = $DataTableCar.NewRow()
  $NewCar["ChassisNumber"]="123456789"
  $NewCar["Brand"]="BMW"
  $NewCar["BuildYear"]=2008
  $DataTableCar.Rows.Add($NewCar)
 
  $NewCar = $DataTableCar.NewRow() 
  $NewCar["ChassisNumber"]="9875151a"
  $NewCar["Brand"]="VW"
  $NewCar["BuildYear"]=2010
  $DataTableCar.Rows.Add($NewCar)
  
  #verkürzte Eingabe von Datensätzen
  $DataTableCar.Rows.Add($Null,"12345","Mercedes",2011) | Out-Null
  $DataTableCar.Rows.Add(200,"123452","ISUZU",1997) | Out-Null
  $DataTableCar.Rows.Add($Null,"1234512","Audi",2007) | Out-Null
 
  Return ,$DataTableCar
} #end of function
 
Main

#Ausgabe

 

original table
 
Index ChassisNumber Brand    BuildYear
----- ------------- -----    ---------
   10 123456789     BMW           2008
   12 9875151a      VW            2010
   14 12345         Mercedes      2011
  200 123452        ISUZU         1997
  202 1234512       Audi          2007
 
copy table
 
Index ChassisNumber Brand    BuildYear
----- ------------- -----    ---------
   10 123456789     BMW           2008
   12 9875151a      VW            2010
   14 12345         Mercedes      2011
  200 123452        ISUZU         1997
  202 1234512       Audi          2007
 
clone table with one imported row
 
Index ChassisNumber Brand BuildYear
----- ------------- ----- ---------
  200 123452        ISUZU      1997

In diesem Beispiel habe ich ein bischen mit den verschiedenen Eigenschaften der DataColumn-Klasse gespielt (AutoIncrement, AutoSeed, MaxLength, Unique, etc.)

Zu Beachten sind die Indexeigenschaften. Autoseed und Autoincrement können auch negativ sein, verhalten sich dann aber etwas anders. Setzt in diesem Beispiel in der Function "New-Datatable" Autoseed und Autoincrement jeweils auf -1 und schaut euch die Ausgaben an.

 

5.2 Aus einer DataTable eine DataView erstellen und umgekehrt

"Stellt eine datenbindungsfähige, angepasste Ansicht einer DataTable zum Sortieren, Filtern, Durchsuchen, Bearbeiten und Navigieren dar."

 

Beispiel 1: Aus einer DataTable eine DataView erzeugen  - Filtern und Sortieren der View

Set-StrictMode -Version "2.0"
Clear-Host
 
Function Main{
  #creates the schema of a datatable
  $myDataTableCar = New-DataTable
  
  #addes some data
  $myDataTableCar = Fill-DataTable $myDataTableCar
 
  #creates dataview 
  $myDataView01 = New-Object System.Data.DataView($myDataTableCar)
 
  #sorting
  "sorting"
  $myDataView01.Sort = "BuildYear DESC"
  $myDataView01 | Format-Table -AutoSize
  #$myDataView01.Sort = "Brand ASC"
  #$myDataView01.Sort = "Brand DESC, BuildingYear ASC"
 
  #filtering
  "filtering: ChassisNumber like '1*' and BuildYear >= 2008" 
 
  $myDataView01.RowFilter = "ChassisNumber like '1*' and BuildYear >= 2008"
  $myDataView01 | Format-Table -AutoSize
} #end of main
 
Function New-DataTable {
 $DataTableCar = New-Object System.Data.DataTable("MyCars")
 
 #creates datafields (=columns)
 $Index = New-Object System.Data.DataColumn("Index")
 $Index.AutoIncrement = $True
 $Index.AutoIncrementSeed = 10
 $Index.AutoIncrementStep = 2
 $DataTableCar.Columns.Add($Index)
 $DataTableCar.PrimaryKey = $DataTableCar.Columns["Index"]
 
 $ChassisNumber = New-Object System.Data.DataColumn("ChassisNumber")
 $ChassisNumber.MaxLength = 25
 $ChassisNumber.Unique = $True
 $ChassisNumber.AllowDBNull = $False
 $ChassisNumber.Caption = "ChassisNumber"
 $DataTableCar.Columns.Add($ChassisNumber)
 
 $Brand = New-Object System.Data.DataColumn("Brand")
 $Brand.MaxLength = 23
 $DataTableCar.Columns.Add($Brand)
 
 $BuildYear = New-Object System.Data.DataColumn("BuildYear","Int32")
 $BuildYear.AllowDBNull = $False
 $DataTableCar.Columns.Add($BuildYear)
 
 Return ,$DataTableCar
 #Return "",$DataTableCar
} #end of function
 
Function Fill-DataTable{
  #adds data (DataRow Objects)
 
  Param($DataTableCar)
 
  $NewCar = $DataTableCar.NewRow()
  $NewCar["ChassisNumber"] = "123456789"
  $NewCar["Brand"]="BMW"
  $NewCar["BuildYear"] = 2008
  $DataTableCar.Rows.Add($NewCar)
 
  $NewCar = $DataTableCar.NewRow() 
  $NewCar["ChassisNumber"] = "9875151a"
  $NewCar["Brand"] = "VW"
  $NewCar["BuildYear"] = 2010
  $DataTableCar.Rows.Add($NewCar)
  
  #verkürzte Eingabe von Datensätzen
  $DataTableCar.Rows.Add($Null,"12345","Mercedes",2011) | Out-Null
  $DataTableCar.Rows.Add(200,"123452","ISUZU",1997) | Out-Null
  $DataTableCar.Rows.Add($Null,"1234512","Audi",2007) | Out-Null
 
  Return ,$DataTableCar
} #end of function
 
Main
#Ausgabe
 

Sort
 
Index ChassisNumber Brand    BuildYear
----- ------------- -----    ---------
   14 12345         Mercedes      2011
   12 9875151a      VW            2010
   10 123456789     BMW           2008
  202 1234512       Audi          2007
  200 123452        ISUZU         1997
 
 
filtering: ChassisNumber like '1*' and BuildYear >= 2008
 
Index ChassisNumber Brand    BuildYear
----- ------------- -----    ---------
   14 12345         Mercedes      2011
   10 123456789     BMW           2008

Zum Erstellen der DataTable benutze ich geanu dieselben Funktionen New-DataTable und Fill-DataTable wie im Beispiel Kapitel 2.1.5 davor

Weitere Beispiele zu RowFilter habe ich auf dieser schönen Seite gefunden:

C# Examples: DataView RowFilter Syntax [C#]

 

Beispiel 2: Eine Dataview in eine neue DataTable exportieren (ToTable-Methode der DataView-Klasse)

Es kann auch manchmal sinnvoll sein, die Resultate eine userdefinierten Filters wieder zurück in eine Datatable zu exportieren

Set-StrictMode -Version "2.0"
Clear-Host
 
Function Main{
  #creates the schema of a datatable
  $myDataTableCar = New-DataTable
  
  #adds some data
  $myDataTableCar = Fill-DataTable $myDataTableCar
 
  #creates dataview 
  $myDataView01 = New-Object System.Data.DataView($myDataTableCar)
 
  #creates dataview
  "showing dataview" 
 
  $myDataView01.RowFilter = "ChassisNumber like '1*' and BuildYear >= 2008"
  $myDataView01 | Format-Table -AutoSize
 
  #creates export-datatable
  "showing datatable"
  $DataTableExport = New-Object System.Data.DataTable("Export")
  $DataTableExport = $myDataView01.ToTable($myDataView01)
  $DataTableExport | Format-Table -AutoSize
 
} #end of main
 
Function New-DataTable {
  #..bitte aus dem Beispiel 1 oben kopieren
}
 
Function Fill-DataTable{
  #..bitte aus dem Beispiel 1 oben kopieren
}

#Ausgabe

 

showing dataview
 
Index ChassisNumber Brand    BuildYear
----- ------------- -----    ---------
   10 123456789     BMW           2008
   14 12345         Mercedes      2011
 
 
showing datatable
 
Index ChassisNumber Brand    BuildYear
----- ------------- -----    ---------
   10 123456789     BMW           2008
   14 12345         Mercedes      2011

 

5.3 Tabellen miteinander in Beziehung setzen

Um, was bei Datenbanken ja normal ist, Tabellen untereinander zu verknüpfen, bietet ein DataSet, das aus mehreren Tabellen besteht, folgende Möglichkeit an

 

Beispiel 1: Zwei Tabellen mit der Relations-Klasse in Beziehung setzen

Set-StrictMode -Version "2.0"
Clear-Host
 
Function Main{
  #creates a dataset
  $DataSet = New-Object System.Data.DataSet 
     
  #creates a table with some data from customers
  New-DataTable_Customer $DataSet | Out-Null
  #Creates a table with some data from cars
  New-DataTable_Car ($DataSet) | Out-Null  
   
  #columns from each table to be related
  $CustomerID_from_Car = $DataSet.Tables.Item("Cars").Columns.Item("CustomerID") 
  $CustomerID_from_Vendor = $DataSet.Tables.Item("Customers").Columns.Item("CustomerID")
  
  #creates the relation 
  $DataRelation = New-Object System.Data.DataRelation("Relation",$CustomerID_from_Vendor,$CustomerID_from_Car)
  $DataSet.Relations.Add($DataRelation) | Out-Null
  #alternative: $DataRelation = $DataSet.Relations.Add("Relation",$CustomerID_from_Vendor,$CustomerID_from_Car)  
 
  #creates related table
  $RelatedTable = @()
  ForEach($ParentRow in $DataRelation.ParentTable.Rows) {
    ForEach($ChildRow in $ParentRow.GetChildRows($DataRelation)) {
      $RelatedTable += New-Object -TypeName PSObject -Property @{
            CustomerID=$ParentRow["CustomerID"];FirstName=$ParentRow["FirstName"];LastName=$ParentRow["LastName"];
            ChassisID=$ChildRow["ChassisNumber"];Brand=$ChildRow["Brand"];BuildYear=$ChildRow["BuildYear"]}
    }
 }
  #Output
  $RelatedTable | Format-Table CustomerID,FirstName,Lastname,Brand,BuildYear,ChassisID -auto
  
} #end of main
 
Function New-DataTable_Car {
 Param($DataSet)
 $DataTableCar = $DataSet.Tables.Add("Cars") 
  
 #creates datafields (=columns)
 $Index = New-Object System.Data.DataColumn("Index")
 $Index.AutoIncrement = $True
 $DataTableCar.Columns.Add($Index)
 $DataTableCar.PrimaryKey = $DataTableCar.Columns["Index"]
 
 $ChassisNumber = New-Object System.Data.DataColumn("ChassisNumber")
 $ChassisNumber.Caption = "ChassisNumber"
 $DataTableCar.Columns.Add($ChassisNumber)
 
 $Brand = New-Object System.Data.DataColumn("Brand")
 $DataTableCar.Columns.Add($Brand)
 
 $BuildYear = New-Object System.Data.DataColumn("BuildYear","Int32")
 $DataTableCar.Columns.Add($BuildYear)
 
 $CustomerID = New-Object System.Data.DataColumn("CustomerID")
 $DataTableCar.Columns.Add($CustomerID)
 
 #$DataTableCar.Rows.Add(Index,ChassisNumber,Brand,BuildYear,CustomerID)
 $DataTableCar.Rows.Add($Null,"123475","VW",2011,"1009") | Out-Null
 $DataTableCar.Rows.Add($Null,"123452","BMW",2010,"1002") | Out-Null
 $DataTableCar.Rows.Add($Null,"123454","Audi",2007,"1005") | Out-Null
 $DataTableCar.Rows.Add($Null,"123456","Mercedes",2011,"1002") | Out-Null
 $DataTableCar.Rows.Add($Null,"123466","ISUZU",1997,"1002") | Out-Null
 $DataTableCar.Rows.Add($Null,"123443","Audi",2014,"1008") | Out-Null
 
 Return ,$DataSet
} #end of function New-DataTable_Car
 
Function New-DataTable_Customer {
 Param($DataSet)
 $DataTableCustomer = $DataSet.Tables.Add("Customers") 
 
 #creates datafields (=columns)
 $Index = New-Object System.Data.DataColumn("Index")
 $Index.AutoIncrement = $True
 $DataTableCustomer.Columns.Add($Index)
 $DataTableCustomer.PrimaryKey = $DataTableCustomer.Columns["Index"]
 
 $Brand = New-Object System.Data.DataColumn("FirstName")
 $DataTableCustomer.Columns.Add($Brand)
 
 $Name = New-Object System.Data.DataColumn("LastName")
 $DataTableCustomer.Columns.Add($Name)
 
 $CustomerID = New-Object System.Data.DataColumn("CustomerID")
 $DataTableCustomer.Columns.Add($CustomerID)
 
 #$DataTableCustomer.Rows.Add(Index,FirstName,LastName,CustomerID)
 $DataTableCustomer.Rows.Add($Null,"Donald","Duck","1000") | Out-Null
 $DataTableCustomer.Rows.Add($Null,"Dagobert","Duck","1002") | Out-Null
 $DataTableCustomer.Rows.Add($Null,"Gustav","Gans","1004") | Out-Null
 $DataTableCustomer.Rows.Add($Null,"Daniel","Düsentrieb","1005") | Out-Null
 $DataTableCustomer.Rows.Add($Null,"Panzerknacker","36-111","1008") | Out-Null
 $DataTableCustomer.Rows.Add($Null,"Klaas","Klever","1009") | Out-Null
 
 Return ,$DataSet
} #End Function New-DataTable_Customer
 
 
Main

#Ausgabe

 

CustomerID FirstName     LastName   Brand    BuildYear ChassisID
---------- ---------     --------   -----    --------- ---------
1002       Dagobert      Duck       BMW           2010 123452   
1002       Dagobert      Duck       Mercedes      2011 123456   
1002       Dagobert      Duck       ISUZU         1997 123466   
1005       Daniel        Düsentrieb Audi          2007 123454   
1008       Panzerknacker 36-111     Audi          2014 123443   
1009       Klaas         Klever     VW            2011 123475

Anmerkungen:

Beide Tabellen besitzen jeweils einen PrimaryKey, was sinnvoll, aber in diesem Beispiel nicht notwendig ist. Die Schlüssel, über die die 1:n Beziehung aufgebaut wird, werden im Constructor der DataRelation-Klasse mitgegeben

 

5.4 Tabellen mergen

 

Die Merge-Methode kommt zum Einsatz, wenn man zwei Tabellen mit gleichem oder ähnlichem Schema vereinen möchte. Beispielsweise kann dies der Fall sein, wenn eine Tabelle laufend mit neuen Werten aktualisiert werden soll.

 

Beispiel 1: Zwei Tabellen kombinieren (mergen)

Set-StrictMode -Version "2.0"
Clear-Host
 
Function Main{
  #creates a new dataset
  $DataSet = New-Object System.Data.DataSet 
   
  #creates a new table with some customerdata
  New-DataTable_Customer $DataSet | Out-Null
  
  #clones $table1 to $table2   
  $Table1 = $DataSet.Tables.Item("Customers")
  $Table2 = $Table1.Clone() #table2 contains no data rather the schema of table1
 
  #adds two new columns to Table2
  $Value1 = New-Object System.Data.DataColumn("Value1")
  $Table2.Columns.Add($Value1)
  $Value2 = New-Object System.Data.DataColumn("Value2")
  $Table2.Columns.Add($Value2)
 
  #changes and adds values including both the new columns
  #$DataTableCustomer.Rows.Add(CustomerID,FirstName,LastName)
  $Table2.Rows.Add(1000,"Oma","Duck",$null,"4711") | Out-Null #changing
  $Table2.Rows.Add(1001,"Dagobert","Duck","4712","4713") | Out-Null #changing
  $Table2.Rows.Add(3000,"Hans","Dampf","5711","5712") | Out-Null  #adding
 
  #sets the Enumeration "MissingSchemaAction" on "Add"
  $MissingSchemaAction = [System.Data.MissingSchemaAction]::Add
 
  #merges Table1 and Table2
  $preserveChanges = $false 
  $MergedTable = $Table1.Copy()
  $MergedTable.Merge($Table2$preserveChanges,$MissingSchemaAction)
 
 
  #output
  Write-Host "Table1" -BackgroundColor DarkYellow
  $Table1 | Format-Table -Auto
 
  Write-Host "Table2" -BackgroundColor DarkYellow
  $Table2 | Format-Table -Auto
 
  Write-Host "MergedTable of Table1 and Table2" -BackgroundColor DarkYellow
  $MergedTable | Format-Table -Auto
} #end of main
 
Function New-DataTable_Customer {
  Param($DataSet)
 $DataTableCustomer = $DataSet.Tables.Add("Customers") 
 
 #creates datafields (=columns)
 $CustomerID = New-Object System.Data.DataColumn("CustomerID")
 $DataTableCustomer.Columns.Add($CustomerID)
 $DataTableCustomer.PrimaryKey = $DataTableCustomer.Columns["CustomerID"]
 
 $First = New-Object System.Data.DataColumn("FirstName")
 $DataTableCustomer.Columns.Add($First)
 
 $LastName = New-Object System.Data.DataColumn("LastName")
 $DataTableCustomer.Columns.Add($LastName)
 
 #$DataTableCustomer.Rows.Add(Index,FirstName,LastName,CustomerID)
 $DataTableCustomer.Rows.Add(1000,"Donald","Duck") | Out-Null
 $DataTableCustomer.Rows.Add(1001,"Dagobert","Duck") | Out-Null
 $DataTableCustomer.Rows.Add(1002,"Gustav","Gans") | Out-Null
 $DataTableCustomer.Rows.Add(1003,"Daniel","Düsentrieb") | Out-Null
 
 Return ,$DataSet
} #End Function New-DataTable_Customer
 
Main

#Ausgabe

 

Table1
 
CustomerID FirstName LastName  
---------- --------- --------  
1000       Donald    Duck      
1001       Dagobert  Duck      
1002       Gustav    Gans      
1003       Daniel    Düsentrieb
 
 
Table2
 
CustomerID FirstName LastName Value1 Value2
---------- --------- -------- ------ ------
1000       Oma       Duck            4711  
1001       Dagobert  Duck     4712   4713  
3000       Hans      Dampf    5711   5712  
 
 
MergedTable of Table1 and Table2
 
CustomerID FirstName LastName   Value1 Value2
---------- --------- --------   ------ ------
1000       Oma       Duck              4711  
1001       Dagobert  Duck       4712   4713  
1002       Gustav    Gans                    
1003       Daniel    Düsentrieb              
3000       Hans      Dampf      5711   5712  
 

Verbunden sind $table1 und $table2 über den PrimaryKey

 

 

5.5 Zeilen von einer Tabelle in eine andere Tabelle kopieren (ImportRow-Methode der DataTable-Klasse)

MSDN: DataTable.ImportRow Method

 

Beispiel 1a: Wer ist oder war mit meinem Rechner verbunden

# this script collects and consolidates all connections to remote computers in a table using "netstat -aon"
 
Clear-Host
Set-StrictMode -Version "2.0"
 
Function Main{
  
  ### setting configuration parameters ###############
 
  #The first delay between the initial connection-analysis and further analyses. Useful for testing e.g. 30
  $InitialDelay = 0 #in seconds. 
 
  #Timedelay between two TCP analysis. About between 30 and 80 seconds are reasonable
  #Duration of this script: $NumberOfAnalyseses * $DelayBetweenEachAnalyis
  $DelayBetweenEachAnalyis = 60 #In seconds. 
  $NumberOfAnalyseses = 2 #How often an analysis will take place 
  
  #sets temp-directory and filename for writing the xml-data
  $TempDirectoryPath = [IO.Path]::GetTempPath()  #or $TempDirectoryPath = "C:\temp"
  $ExportFileName = "TcpConnections1"
 
  #some other scriptparameters
  $ScreenOutput = $True #shows the final result on screen
  $TestMode = $True  #False | $True : #shows results in between, too
  $FileOutput = $True #False | $True : #writes results in xmlFiles needed for analysis of DnsHostname e.g.
  
  ### end of setting configuration parameters ###############
 
 
  #FunctionCall: creates the mainTable collecting all connections
  $DataTableMain = Create-ConnectionTable "Main"
  
  #FunctionCall: fills the mainTable with the initial connections    
  $DataTableMain = $(Execute-Netstat $DataTableMain )[-1]
 
  If ($Testmode -eq $True){
     Write-Host "Initial Table"
     $DataTableMain |  Format-Table  -AutoSize
  }
 
  Start-Sleep -s $InitialDelay 
    
  For($i = 1;$i -lt $NumberOfAnalyseses;$i++){
    Start-Sleep -s $DelayBetweenEachAnalyis
 
    #FunctionCalls: Create a temporary table and fill it with current connectiondata
    $DataTableTemp = Create-ConnectionTable "Temp"
    $DataTableTemp = $(Execute-Netstat $DataTableTemp)[-1]
    
   #every row from the tempRable will be checked by using the primarykey-array against the maintable.
   #the row of the tempTable only will be added to the mainTable, if it doesn't already exist
    ForEach($Row in $DataTableTemp.Rows){
      Try{
         #check if $Row from $DataTableTemp already exists in $DataTableMain
         [void]$DataTableMain.Rows.Find($Row[@("Localport","State","PID")])[0] 
      }Catch{ 
         #$Row from $DataTableTemp doesn't exist in $DataTableMain and will be added
         $DataTableMain.ImportRow($Row)  
      }
    }#Foreach
    
    #Testmode $True or $False
    If ($Testmode){
      Write-Host "TempTable $i"
      $DataTableTemp | Format-Table -AutoSize
    }
  }#for $i
     
    Write-Host "Final Table"
    $DataTableMainView = New-Object System.Data.DataView($DataTableMain)
    $DataTableMainView.Sort = "Localport"
    $DataTableMainView |  Format-Table -AutoSize
 
     Write-XMLFile $TempDirectoryPath $ExportFileName $DataTableMain
     Write-Host "File $ExportFileName is saved to $TempDirectoryPath"
}#Main
 
 
Function Execute-Netstat{
  #see: PowerShell Code Repository   http://poshcode.org/560 
 
  Param($DataTable)
   
  $netstat = netstat -a -n -o
  [regex]$regexTCP = '(?<Protocol>\S+)\s+((?<LAddress>(2[0-4]\d|25[0-5]|[01]?\d\d?)\.(2[0-4]\d|25[0-5]|[01]?\d\d?)\.(2[0-4]\d|25[0-5]|[01]?\d\d?)\.(2[0-4]\d|25[0-5]|[01]?\d\d?))|(?<LAddress>\[?[0-9a-fA-f]{0,4}(\:([0-9a-fA-f]{0,4})){1,7}\%?\d?\]))\:(?<Lport>\d+)\s+((?<Raddress>(2[0-4]\d|25[0-5]|[01]?\d\d?)\.(2[0-4]\d|25[0-5]|[01]?\d\d?)\.(2[0-4]\d|25[0-5]|[01]?\d\d?)\.(2[0-4]\d|25[0-5]|[01]?\d\d?))|(?<RAddress>\[?[0-9a-fA-f]{0,4}(\:([0-9a-fA-f]{0,4})){1,7}\%?\d?\]))\:(?<RPort>\d+)\s+(?<State>\w+)\s+(?<PID>\d+$)'
    
  [regex]$regexUDP = '(?<Protocol>\S+)\s+((?<LAddress>(2[0-4]\d|25[0-5]|[01]?\d\d?)\.(2[0-4]\d|25[0-5]|[01]?\d\d?)\.(2[0-4]\d|25[0-5]|[01]?\d\d?)\.(2[0-4]\d|25[0-5]|[01]?\d\d?))|(?<LAddress>\[?[0-9a-fA-f]{0,4}(\:([0-9a-fA-f]{0,4})){1,7}\%?\d?\]))\:(?<Lport>\d+)\s+(?<RAddress>\*)\:(?<RPort>\*)\s+(?<PID>\d+)'
           
  foreach ($net in $netstat)
      {
        switch -regex ($net.Trim())
       {
          $regexTCP
          {          
            $Protocol = $matches.Protocol
            $LocalAddress = $matches.LAddress
            $Localport = $matches.LPort
            $RemoteAddress = $matches.RAddress
            $Remoteport = $matches.RPort
            $State = $matches.State
            $myPID = $($matches.PID) -as [int]
            $ProcessName = ( Get-Process -Id $matches.PID ).ProcessName
            $TimeStamp = $([System.DateTime]::Now)
            
                If ( ($RemoteAddress -ne "0.0.0.0") -and  ($RemoteAddress -ne "127.0.0.1") `
                     -and  ($RemoteAddress -ne '[::]') -and  ($RemoteAddress -ne '[::1]') -and  ($RemoteAddress -ne '*') ){
                    $DataTable.Rows.Add($Protocol,$LocalAddress,$LocalPort,$RemoteAddress,$RemotePort,$State,$myPID,$ProcessName,$TimeStamp)
                } #if
 
         }
         $regexUDP
         {          
            $Protocol = $matches.Protocol
            $LocalAddress = $matches.LAddress
            $Localport = $matches.LPort
            $RemoteAddress = $matches.RAddress
            $Remoteport = $matches.RPort
            $State = "_"#$matches.State
            $myPID = $($matches.PID) -as [int]
            $ProcessName = ( Get-Process -Id $matches.PID ).ProcessName
            $TimeStamp = $([System.DateTime]::Now)
            
                If ( ($RemoteAddress -ne "0.0.0.0") -and  ($RemoteAddress -ne "127.0.0.1") `
                   -and  ($RemoteAddress -ne '[::]') -and  ($RemoteAddress -ne '[::1]') -and  ($RemoteAddress -ne '*') ){
                   $DataTable.Rows.Add($Protocol,$LocalAddress,$LocalPort,$RemoteAddress,$RemotePort,$State,$myPID,$ProcessName,$TimeStamp)
                } #if
        }
     }#switch
   }#foreach
   Return ,$DataTable # ,$DataTable to keep the datatable-type
}#end Function
 
Function Get-CurrentConnections_DataTable{
   Param($DataTable,$TempDirectoryPath,$ExportFileName)
   $IPProperties =  [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties()
   $ActiveTCPConnections = $($IPProperties.GetActiveTcpConnections()) 
      
   $ActiveTCPConnections | Foreach{
      $RemoteAddress = $($_.RemoteEndPoint).Address.IPAddressToString
 
      #Nur bei der erten MainTable generell für jede IPAdresse den RemoteNamen abfragen
      $LocalAddress = $($_.LocalEndPoint).Address.IPAddressToString
      $LocalPort = $($_.LocalEndPoint).Port
      $RemotePort = $($_.RemoteEndPoint).Port
      $TimeStamp = $([System.DateTime]::Now)
      If ( ($RemoteAddress -ne "127.0.0.1") -and  ($RemoteAddress -ne "::1") ){
        $DataTable.Rows.Add($_.State,$LocalAddress,$LocalPort,$RemoteAddress,$RemotePort,$TimeStamp)
      } #if
   }#Foreach
   
   Return ,$DataTable # ,$DataTable to keep the datatable-type
} #Function Get-Currentconnections
 
Function Create-ConnectionTable{
 Param($TableName)
 
 $DataTable = New-Object System.Data.DataTable
 $DataTable.TableName = $TableName
 
 $ColumnNames = @("Protocol","LocalAddress","LocalPort","RemoteAddress","RemotePort","State","PID","ProcessName","TimeStamp")
 
 #Add Columns to the DataTable
  ForEach($ColumnName in $ColumnNames){
    $Column = New-Object System.Data.DataColumn($ColumnName)
      $DataTable.Columns.Add($Column)
  } #foreach ColumnName
 
 $PrimaryKeys = @()
   $DataTable.PrimaryKey = $DataTable.Columns[@("Localport","State","PID")] 
  
 Return ,$DataTable
} #Function Create-ConnectionTable
 
Function Write-XMLFile{
  Param($TempDirectoryPath,$FileName,$DataTable1)
  $XMLPath = "$TempDirectoryPath$FileName.xml"  #xml-file
  $XSDPath = "$TempDirectoryPath$FileName.xsd"  #xml-schema
  $XMLStream = [System.Io.StreamWriter]$XMLPath
  $XSDStream = [System.Io.StreamWriter]$XSDPath
  $DataTable1.WriteXML($XMLStream)
  $DataTable1.WriteXmlSchema($XSDStream)
  $XSDStream.Close()
  $XMLStream.Close()
 }
 
Main  
 

#mögliche Ausgabe

 

Final Table
 
Protocol LocalAddress   LocalPort RemoteAddress  RemotePort State       PID  ProcessName     TimeStamp          
-------- ------------   --------- -------------  ---------- -----       ---  -----------     ---------          
TCP      192.168.178.57 62846     157.56.124.87  443        ESTABLISHED 1860 explorer        13.09.2014 16:20:19
TCP      192.168.178.57 62928     213.165.67.38  443        CLOSE_WAIT  3780 DAVSRV          13.09.2014 16:20:19
TCP      192.168.178.57 62939     173.194.65.188 5228       ESTABLISHED 5032 chrome          13.09.2014 16:20:19
TCP      192.168.178.57 63036     74.125.136.125 5222       ESTABLISHED 4560 googledrivesync 13.09.2014 16:20:19
...
File TcpConnections1 is saved to C:\Users\kaiusr\AppData\Local\Temp\

In dieser Konfiguration läuft das Skript etwa 2 Minuten und legt im Temp-Path des Users zwei XML-Files mit den erhaltenen Daten an. Diese Daten können mit dem Skript im nächsten Beispiel 1b direkt weiterverarbeitet werden.

 

Beispiel 1b: Auflösen der RemoteHostIP-Adresse

(Bitte erst ausführen, nachdem mit dem Skript aus Beispiel 1a die XML-Files angelegt wurden!)

#this sripts completes the table of the previous script with the DNSName of the remoteaddress
 
Clear-Host
Set-StrictMode -Version "2.0"
 
Function Main{
 
  ### setting configuration parameters ###############
 
  #set temp-directory for writing the xml-data
  $ScreenOutput = $True
  $FileOutput = $True
  $TestMode = $True
  $TempDirectoryPath = [IO.Path]::GetTempPath()  #or $TempDirectoryPath = "C:\temp"
  $ImportFileName = "TcpConnections1" #import without DNSHostnames
  $ExportFileName = "TcpConnections2" #export with DNSHostnames
  ### end of setting configuration parameters ###############
 
  #Reading data from XML to datatable and adding "DNSHostName
  $DataTable = Read-XMLFile $ImportFileName $TestMode
 
  #Export new datatable to xmlFiles 
  If ($FileOutput){
    Write-XMLFile $TempDirectoryPath $ExportFileName $DataTable
   }
 
  #Output on screen
  If ($ScreenOutput){
    $DataTable | Format-Table -auto
    
  }
 
  If ($FileOutput){
    Write-Host "Writing XMLValues.xml and XMLSchema.xsd to $TempDirectoryPath has finished"
    }
}
 
Function Read-XMLFile{
  <#
  .SYNOPSIS
  a) import the xmlFile created during "NetWorkInformation.ps1" to a DataTable using a FileObject
  b) adding the new columns "DNSHostname to $Datatable and filling it with the result of the function "Get-DNSHostName" 
  c) The ReturnValue is the amended by "DnsHostname" datatable
  #>
  
  Param($ImportFileName,$TestMode)
   
  #adding the column ("DNSHostName") to $DataTable 
  $DataTable = New-Object System.Data.DataTable #empty
  $XMLPath = "$TempDirectoryPath$ImportFilename.xml"  #xml-file
  $XSDPath = "$TempDirectoryPath$ImportFilename.xsd"  #xml-schema
  $XMLFile = [System.Io.File]::OpenRead($XMLPath)
  $XSDFile = [System.Io.File]::OpenRead($XSDPath)
  $DataTable = New-Object System.Data.DataTable
  $DataTable.ReadXmlSchema($XSDFile)
  [void]$DataTable.ReadXml($XMLFile) 
  $XMLFile.Close()
  $XSDFile.Close()
  [Void]$DataTable.Columns.Add("DNSHostName")
  
  $NumberOfRows = $DataTable.Rows.Count
  $DNSHostName = @()
  For ($i = 0; $i -lt $($DataTable.Rows.count); $i++){
    $Row = $DataTable.Rows[$i] 
    If ($i -ne 0){
      
      If (  $Row.RemoteAddress -eq $DataTable.Rows[$i-1].Item("RemoteAddress") ){
      $DNSHostName = $DataTable.Rows[$i-1].Item("DNSHostName")
      }Else{
      $DNSHostName = Get-DNSHostName $Row.RemoteAddress
      } #If (  $Row.RemoteAddress....
 
    }Else{ #$i -ne 0
      $DNSHostName = Get-DNSHostName $Row.RemoteAddress
    }#$i -ne 0
    
    If ($TestMode){
       Write-Host "$($i+1) von $NumberOfRows : $($Row.RemoteAddress) -> $DNSHostName"
    }
    $Row["DNSHostName"] = $DNSHostName
  }#For
  Return ,$DataTable
}#end of Function Read-XMLFile
 
Function Get-DNSHostName{
  <#
  .Synopsis
  tring to resolve an ipaddress to a dnshostname
  #>
  Param($RemoteAddress)
    Try{
      $RemoteHostName = $([System.Net.Dns]::GetHostEntry($RemoteAddress)).HostName
    }Catch{
      $RemoteHostName ="Unknown Hostname"
    }#Try/ Catch
    Return $RemoteHostName
}#Function
 
Function Write-XMLFile{
  <#
  .Synopsis
  Export a datatable to "$TempDirectoryPath\$FileName "
  #>
  Param($TempDirectoryPath,$FileName,$DataTable1)
  $XMLPath = "$TempDirectoryPath\$FileName.xml"  #xml-file
  $XSDPath = "$TempDirectoryPath\$FileName.xsd"  #xml-schema
  $XMLStream = [System.Io.StreamWriter]$XMLPath
  $XSDStream = [System.Io.StreamWriter]$XSDPath
  $DataTable1.WriteXML($XMLStream)
  $DataTable1.WriteXmlSchema($XSDStream)
  $XSDStream.Close()
  $XMLStream.Close()
}
 
Main

#mögliche Ausgabe

 

Protocol LocalAddress         LocalPort RemoteAddress         RemotePort State       PID  ProcessName     TimeStamp           DNSHostName                                       
-------- ------------         --------- -------------         ---------- -----       ---  -----------     ---------           -----------      
TCP      192.168.178.57       62846     157.56.124.87         443        ESTABLISHED 1860 explorer        13.09.2014 16:56:19 db.wn.windows.com 
TCP      192.168.178.57       62928     213.165.67.38         443        CLOSE_WAIT  3780 DAVSRV          13.09.2014 16:56:19 sd2dav.1und1.de 
TCP      192.168.178.57       62939     173.194.65.188        5228       ESTABLISHED 5032 chrome          13.09.2014 16:56:19 ee-188.1e100.net
TCP      192.168.178.57       63036     74.125.136.125        5222       ESTABLISHED 4560 googledrivesync 13.09.2014 16:56:19 ea.1e100.net 
TCP      192.168.178.57       63140     77.234.42.64          80         ESTABLISHED 1392 AvastSvc        13.09.2014 16:56:19 r.ff.avast.com 
 

Die erstellten XML-Files lassen sich übrigens direkt in Excel importieren.

 

5.6 DataSets und DataTables Serialisieren und Deserialisieren (=Export und Import nach und von XML-Dateien)

Beispiel 1: Beispieldaten und Schema einer DatenTabelle in XML-Files exportieren und wieder importieren

Set-StrictMode -Version "2.0"
Clear-Host
 
Function Main{
  #creating a new table with some customer data
  $Table1 = New-DataTable 
  
  #output
  Write-Host "Table1" -BackgroundColor DarkYellow
  $Table1 | Format-Table -Auto
 
  #serializing table1 by using a StreamWriter-Object
  $XMLPath = "C:\temp\table1.xml"  #xml-file
  $XSDPath = "C:\temp\table1.xsd"  #xml-schema
  $XMLStream = [System.Io.StreamWriter]$XMLPath
  $XSDStream = [System.Io.StreamWriter]$XSDPath
  $Table1.WriteXML($XMLStream)
  $Table1.WriteXmlSchema($XSDStream)
  $XSDStream.Close()
  $XMLStream.Close()
 
  <# 
  #serializing table1 by using a File-Object
  $XMLPath = "C:\temp\table1.xml"  #xml-file
  $XSDPath = "C:\temp\table1.xsd"  #xml-schema
  $XMLFile = [System.Io.File]::Create($XMLPath)
  $XSDFile = [System.Io.File]::Create($XSDPath)
  $Table1.WriteXML($XMLFile)
  $Table1.WriteXmlSchema($XSDFile)
  $XMLFile.Close()
  $XSDFile.Close()
  #>
 
  #desirializing a xmlFile to $Table2 using a FileObject
  $Table2 = New-Object System.Data.DataTable #still empty
  $XMLPath = "C:\temp\table1.xml"  #xml-file
  $XSDPath = "C:\temp\table1.xsd"  #xml-schema
  $XMLFile = [System.Io.File]::OpenRead($XMLPath)
  $XSDFile = [System.Io.File]::OpenRead($XSDPath)
  $Table2 = New-Object System.Data.DataTable
  $Table2.ReadXmlSchema($XSDFile)
  [void]$Table2.ReadXml($XMLFile) 
  $XMLFile.Close()
  $XSDFile.Close()
 
  Write-Host "deserialized Table2" -BackgroundColor DarkYellow
  $Table2 | ft -Auto
  
} #end of main
 
Function New-DataTable{
  $DataTableCustomer = New-Object System.Data.DataTable 
  $DataTableCustomer.TableName = "customers"
 
  #creating datafields (=columns)
  $CustomerID = New-Object System.Data.DataColumn("CustomerID")
  $DataTableCustomer.Columns.Add($CustomerID)
  $DataTableCustomer.PrimaryKey = $DataTableCustomer.Columns["CustomerID"]
 
  $First = New-Object System.Data.DataColumn("FirstName")
  $DataTableCustomer.Columns.Add($First)
 
  $LastName = New-Object System.Data.DataColumn("LastName")
  $DataTableCustomer.Columns.Add($LastName)
 
  #$DataTableCustomer.Rows.Add(Index,FirstName,LastName,CustomerID)
  $DataTableCustomer.Rows.Add(1000,"Donald","Duck") | Out-Null
  $DataTableCustomer.Rows.Add(1001,"Dagobert","Duck") | Out-Null
  $DataTableCustomer.Rows.Add(1002,"Gustav","Gans") | Out-Null
  $DataTableCustomer.Rows.Add(1003,"Daniel","Düsentrieb") | Out-Null
 
  Return ,$DataTableCustomer
} #End Function New-DataTable_Customer
 
Main

#Ausgabe

 

Table1
 
CustomerID FirstName LastName  
---------- --------- --------  
1000       Donald    Duck      
1001       Dagobert  Duck      
1002       Gustav    Gans      
1003       Daniel    Düsentrieb
 
 
deserialized Table2
 
CustomerID FirstName LastName  
---------- --------- --------  
1000       Donald    Duck      
1001       Dagobert  Duck      
1002       Gustav    Gans      

 

1003       Daniel    Düsentrieb

 

Das Skript erzeugt eine xml-Datei c:\temp\table1.xml sowie auch die zugehörige Schemadatei c:\temp\table1.xsd. 

Wie im weiteren Skript gezeigt können diese Files (Data(*.xml)+Schema(*.xsd) wieder in eine Datatable importiert werden. Ebenso kann man die xml-Datei aber auch direkt beispielsweise nach Excel importieren

 

5.7 DataTables in Funktionen

Beispiel 1: DataTable in einer Funktion erstellen und als Returnwert zurüclgeben

Clear-Host
Set-StrictMode -Version "2.0"
 
Function Main{
  $DataTable = New-DataTable
 
  $DataTable[-1].GetType() #DataTable !!!
     $DataTable.GetType()     #Arra< !!!
  $DataTable[-1].TableName
  $DataTable[-1] | Format-Table
  #$DataTable.TableName #doesn't work"
} #end Main
 
Function New-DataTable{
  $myDataTable = New-Object System.Data.DataTable
  $myDataTable.TableName = "TestTable"
  $Column1 = New-Object System.Data.DataColumn("FullName")
  $myDataTable.Columns.Add($Column1)
  $myDataTable.Rows.Add("Micky Maus")
  Return ,$myDataTable
} End New-DataTable
 
Main
IsPublic IsSerial Name                              BaseType 
-------- -------- ----                              --------                                      
True     True     DataTable                         System.ComponentModel.MarshalByValueComponent                                                                                      
TestTable
 
FullName                                                                           
--------                                                                           
Micky Maus    

 

Ein wenig seltsam verhält sich eine DataTable, wenn Sie - wie in diesem Beispiel- in einer untergeordneten Function erstellt und mit Werten gefüllt wird. In der aufrufenden Funktion ist das zurückerhaltene Objekt dann vom Typ "Array", was sich störend auswirken kann. Um weiter ein DataTable-Objekt mit seinen Stärken zur Verfügung zu haben, benutze ich $DataTable[-1]

Ich kann das Konstrukt nicht erklären, ich habe es selbst nur durch Probieren mehr oder weniger zufällig als Workaround gefunden.

 

5.8 DataTables anstelle von Arrays und HashTabellen benutzen

Man kann DataTables anstelle von Arrays oder Hashtabellen verwenden und hat hier besonders bei mehreren Spalten enorme Vorteile.

 

Beispiel 1: Eine DataTable ähnlich einem Array benutzen

Set-StrictMode -Version "2.0"
Clear-Host


#----Anlage einer Beispiel-Tabelle mit einer einzigen Spalte----
$DataTable=New-Object System.Data.DataTable("MyFriends")
$Index = New-Object System.Data.DataColumn("Index")
$Vorname = New-Object System.Data.DataColumn("Vorname")
$Nachname = New-Object System.Data.DataColumn("Nachname")
$DataTable.Columns.Add($Index)
$DataTable.Columns.Add($Vorname)
$DataTable.Columns.Add($Nachname)

#Anhängen von Datensätzen
$DataTable.Rows.Add(0,"Donald","Duck") | out-null
$DataTable.Rows.Add(1,"Karl","Napf") | out-null
$DataTable.Rows.Add(2,"Speedy","Gonzales") | out-null
$DataTable.Rows.Add(3,"Tom","Cat") | out-null
$DataTable.Rows.Add(4,"Jerry","Mouse") | out-null
#----Ende der Anlage ----


""
#1. Jede Zeile in einer Schleife ansprechen
$String = "1. Jeden Datensatz in einer Schleife ansprechen"
Write-Host  $String -BackgroundColor DarkRed -ForegroundColor DarkYellow
$DataTable.Rows | Foreach{
 "{0} {1} {2}" -f $_.Index,$_.Vorname,$_.Nachname
  #$_  #identisch
}

""
#2. Sortieren der Tabelle nach einer Spalte
$String="2. Sortieren mit der Select-Methode"
Write-Host $String -BackgroundColor DarkRed -ForegroundColor DarkYellow
#$SortedDataTable=$DataTable.Select($null,"Nachname")
$SortedDataTable=$DataTable.Select($null,$Nachname) #identisch
$SortedDataTable | Format-Table Index,Vorname,NachName -autosize
 
#3. Die Zeile mit der OrderNummer 2 ausgeben
$String = "3. eine bestimmte Zeile ausgeben"
Write-Host $String -BackgroundColor DarkRed -ForegroundColor DarkYellow
$DataTable.Rows[2] | Format-Table -AutoSize

#4. Die Zeile mit der OrderNummer 0,1 und 4 ausgeben
$String = "4. mehrere bestimmte Zeilen ausgeben"
Write-Host $String -BackgroundColor DarkRed -ForegroundColor DarkYellow
$DataTable.Rows[0,1,4] | Format-Table -AutoSize

#5. Spalte Vorname in Rows[2] ausgeben
$String = "5. eine Zeile einer Spalte ausgeben"
Write-Host  $String -BackgroundColor DarkRed -ForegroundColor DarkYellow
$DataTable.Rows[2].Vorname
#identisch: $DataTable.Rows[2][1]

#6. Spalte Vorname in Rows 0,1,2,3,4 ausgeben

$String = "6. mehrere Zeile(n) einer Spalte ausgeben"
Write-Host  $String -BackgroundColor DarkRed -ForegroundColor DarkYellow
$DataTable.Rows[0,1,2,3,4].Vorname
#Ausgabe

1. Jeden Datensatz in einer Schleife ansprechen
0 Donald Duck
1 Karl Napf
2 Speedy Gonzales
3 Tom Cat
4 Jerry Mouse

2. Sortieren mit der Select-Methode
Index Vorname Nachname
----- ------- --------
3     Tom     Cat     
0     Donald  Duck    
2     Speedy  Gonzales
4     Jerry   Mouse   
1     Karl    Napf    

3. eine bestimmte Zeile ausgeben
Index Vorname Nachname
----- ------- --------
2     Speedy  Gonzales

4. mehrere Zeilen ausgeben
Index Vorname Nachname
----- ------- --------
0     Donald  Duck    
1     Karl    Napf    
4     Jerry   Mouse   

5. mehreren Zeile(n) einer Spalte ausgeben
Speedy


6. mehreren Zeilen einer Spalte ausgeben  
Donald
Karl
Speedy
Tom
Jerry

Natürlich haben auch Arrays ihre Vorteile und Einsatzgebiete, siehe MSDN: about_arrays .Wenn man mehr als eine Spalte an Daten in einer Struktur verwalten möchte, dann sind DataTables die geeignete Struktur.

 

Beispiel 2: Mit der Find-Methode eine DataTable ähnlich einer Hashtabelle nutzen

Auf die Find-Methode gehe ich weiter unten nochmal etwas genauer ein

Set-StrictMode -Version "2.0"
Clear-Host
 
#Anlage einer DataTabelle mit einer einzigen Spalte
$DataTable=New-Object System.Data.DataTable("MyFriends")
$Index = New-Object System.Data.DataColumn("Index")
$Vorname = New-Object System.Data.DataColumn("Vorname")
$Nachname = New-Object System.Data.DataColumn("Nachname")
$DataTable.Columns.Add($Index)
$DataTable.Columns.Add($Vorname)
$DataTable.Columns.Add($Nachname)

#Anhängen von Datensätzen
$DataTable.Rows.Add(0,"Donald","Duck") | out-null
$DataTable.Rows.Add(1,"Karl","Napf") | out-null
$DataTable.Rows.Add(2,"Speedy","Gonzales") | out-null
$DataTable.Rows.Add(3,"Tom","Cat") | out-null
$DataTable.Rows.Add(4,"Jerry","Mouse") | out-null
#----Ende der Anlage ----

#Einen Datensatz mit der Findmethode finden
$String = "Eine ganze DatenReihe mit der Find-Methode suchen"
Write-Host $String -BackgroundColor DarkRed -ForegroundColor DarkYellow
#Die Find Methode benötigt einen Primarykey in der Tabelle
$DataTable.PrimaryKey = $DataTable.Columns["Vorname"]
$DataTable.Rows.Find("Tom") | FT -AutoSize

#DataTable nach dem Vornamen "Karl" suchen und die zugehörige Spalte Nachname ausgeben
$String = "Key/ Value - Abfrage mit der Find-Methode"
Write-Host $String -BackgroundColor DarkRed -ForegroundColor DarkYellow
$Vorname="Karl" #Key  
$DataTable.Rows.Find($Vorname)["Nachname"] #Value
#Ausgabe

Eine ganze DatenReihe mit der Find-Methode suchen

Index Vorname Nachname
----- ------- --------
3     Tom     Cat     

Key/ Value - Abfrage mit der Find-Methode
Napf

Normale Hashtabellen, siehe MSDN: about_Hash_Tables sind die geeignete Strukur für einfache Key/ Value Paare. Mit Datatables ist es dagegen wiederum möglich, Strukturen aus mehr als einem Value zu verwalten.