Freigeben über


Erstellung eines Windows-PowerShell-Inhaltsanbieters

Dieses Thema beschreibt, wie man einen Windows-PowerShell-Anbieter erstellt, der es dem Benutzer ermöglicht, den Inhalt der Elemente in einem Datenspeicher zu manipulieren. Folglich wird ein Anbieter, der den Inhalt von Elementen manipulieren kann, als Windows PowerShell-Inhaltsanbieter bezeichnet.

Hinweis

Sie können die C#-Quelldatei (AccessDBSampleProvider06.cs) dieses Anbieters mit dem Microsoft Windows Software Development Kit für Windows Vista und .NET Framework 3.0 Runtime Components herunterladen. Für Download-Anweisungen siehe Wie man Windows PowerShell installiert und das Windows PowerShell SDK herunterlädt. Die heruntergeladenen Quellcodedateien sind im <PowerShell-Samples-Verzeichnis> verfügbar. Weitere Informationen zu anderen Implementierungen von Windows PowerShell-Anbietern finden Sie unter Designing Your Windows PowerShell Provider.

Definiere die Windows PowerShell Content Provider-Klasse

Ein Windows-PowerShell-Inhaltsanbieter muss eine .NET-Klasse erstellen, die die System.Management.Automation.Provider.IContentCmdletProvider-Schnittstelle unterstützt. Hier ist die Klassendefinition für den in diesem Abschnitt beschriebenen Artikelanbieter.

[CmdletProvider("AccessDB", ProviderCapabilities.None)]
public class AccessDBProvider : NavigationCmdletProvider, IContentCmdletProvider

Beachten Sie, dass in dieser Klassendefinition das Attribut System.Management.Automation.Provider.CmdletProviderAttribute zwei Parameter enthält. Der erste Parameter gibt einen benutzerfreundlichen Namen für den Anbieter an, der von Windows PowerShell verwendet wird. Der zweite Parameter spezifiziert die Windows-PowerShell-spezifischen Fähigkeiten, die der Anbieter während der Befehlsverarbeitung der Windows-PowerShell-Laufzeit bereitstellt. Für diesen Anbieter gibt es keine zusätzlichen Windows-PowerShell-spezifischen Funktionen.

Definiere die Funktionalität der Basisklasse

Wie in Design Your Windows PowerShell Provider beschrieben, leitet sich die Klasse System.Management.Automation.Provider.NavigationCmdletProvider von mehreren anderen Klassen ab, die unterschiedliche Provider-Funktionalitäten bereitstellten. Ein Windows-PowerShell-Inhaltsanbieter definiert daher typischerweise alle von diesen Klassen bereitgestellten Funktionalitäten.

Für weitere Informationen darüber, wie man Funktionalitäten zum Hinzufügen sessionsspezifischer Initialisierungsinformationen und zur Freigabe von Ressourcen, die vom Anbieter genutzt werden, implementiert werden kann, siehe Creating a Basic Windows PowerShell Provider. Die meisten Anbieter, einschließlich des hier beschriebenen Anbieters, können jedoch die Standardimplementierung dieser Funktionalität verwenden, die von Windows PowerShell bereitgestellt wird.

Um auf den Datenspeicher zuzugreifen, muss der Anbieter die Methoden der Basisklasse System.Management.Automation.Provider.DriveCmdletProvider implementieren. Weitere Informationen zur Implementierung dieser Methoden finden Sie unter Erstellung eines Windows PowerShell Laufwerksanbieters.

Um die Elemente eines Datenspeichers zu manipulieren, wie das Empfangen, Setzen und Löschen von Elementen, muss der Anbieter die von der System.Management.Automation.Automation.Provider.ItemCmdletProvider-Basisklasse bereitgestellten Methoden implementieren. Weitere Informationen zur Implementierung dieser Methoden finden Sie unter Erstellung eines Windows PowerShell Item Provider.

Um an mehrschichtigen Datenspeichern zu arbeiten, muss der Anbieter die Methoden implementieren, die von der Basisklasse System.Management.Automation.Provider.ContainerCmdletProvider bereitgestellt werden. Weitere Informationen zur Implementierung dieser Methoden finden Sie unter Erstellung eines Windows PowerShell Container Provider.

Um rekursive Befehle, verschachtelte Container und relative Pfade zu unterstützen, muss der Anbieter die Basisklasse System.Management.Automation.Provider.NavigationCmdletProvider implementieren. Zusätzlich kann dieser Windows-PowerShell-Inhaltsanbieter System.Management.Automation.Provider.IContentCmdletProvider-Schnittstelle an die Basisklasse System.Management.Automation.Provider.NavigationCmdletProvider anhängen und muss daher die von dieser Klasse bereitgestellten Methoden implementieren. Weitere Informationen finden Sie unter Implementierung dieser Methoden unter Implementierung eines Navigations-Windows-PowerShell-Anbieters.

Implementierung eines Content Readers

Um Inhalte von einem Element zu lesen, muss ein Anbieter eine Content Reader-Klasse implementieren, die von System.Management.Automation.Provider.IContentReader abgeleitet ist. Der Inhaltsleser dieses Anbieters ermöglicht den Zugriff auf die Inhalte einer Zeile in einer Datentabelle. Die Content Reader-Klasse definiert eine Read-Methode , die die Daten aus der angegebenen Zeile abruft und eine Liste zurückgibt, die diese Daten repräsentiert, eine Seek-Methode , die den Content Reader bewegt, eine Close-Methode , die den Content Reader schließt, und eine Dispose-Methode .

public class AccessDBContentReader : IContentReader
{
    // A provider instance is required so as to get "content"
    private AccessDBProvider provider;
    private string path;
    private long currentOffset;

    internal AccessDBContentReader(string path, AccessDBProvider provider)
    {
        this.path = path;
        this.provider = provider;
    }

    /// <summary>
    /// Read the specified number of rows from the source.
    /// </summary>
    /// <param name="readCount">The number of items to 
    /// return.</param>
    /// <returns>An array of elements read.</returns>
    public IList Read(long readCount)
    {
        // Read the number of rows specified by readCount and increment
        // offset
        string tableName;
        int rowNumber;
        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        Collection<DatabaseRowInfo> rows =
            provider.GetRows(tableName);
        Collection<DataRow> results = new Collection<DataRow>();

        if (currentOffset < 0 || currentOffset >= rows.Count)
        {
            return null;
        }

        int rowsRead = 0;

        while (rowsRead < readCount && currentOffset < rows.Count)
        {
            results.Add(rows[(int)currentOffset].Data);
            rowsRead++;
            currentOffset++;
        }

        return results;
    } // Read

    /// <summary>
    /// Moves the content reader specified number of rows from the 
    /// origin
    /// </summary>
    /// <param name="offset">Number of rows to offset</param>
    /// <param name="origin">Starting row from which to offset</param>
    public void Seek(long offset, System.IO.SeekOrigin origin)
    {
        // get the number of rows in the table which will help in
        // calculating current position
        string tableName;
        int rowNumber;

        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        if (type == PathType.Invalid)
        {
            throw new ArgumentException("Path specified must represent a table or a row :" + path);
        }

        if (type == PathType.Table)
        {
            Collection<DatabaseRowInfo> rows = provider.GetRows(tableName);

            int numRows = rows.Count;

            if (offset > rows.Count)
            {
                throw new
                       ArgumentException(
                           "Offset cannot be greater than the number of rows available"
                                        );
            }

            if (origin == System.IO.SeekOrigin.Begin)
            {
                // starting from Beginning with an index 0, the current offset
                // has to be advanced to offset - 1
                currentOffset = offset - 1;
            }
            else if (origin == System.IO.SeekOrigin.End)
            {
                // starting from the end which is numRows - 1, the current
                // offset is so much less than numRows - 1
                currentOffset = numRows - 1 - offset;
            }
            else
            {
                // calculate from the previous value of current offset
                // advancing forward always
                currentOffset += offset;
            }
        } // if (type...
        else
        {
            // for row, the offset will always be set to 0
            currentOffset = 0;
        }

    } // Seek

    /// <summary>
    /// Closes the content reader, so all members are reset
    /// </summary>
    public void Close()
    {
        Dispose();
    } // Close

    /// <summary>
    /// Dispose any resources being used
    /// </summary>
    public void Dispose()
    {
        Seek(0, System.IO.SeekOrigin.Begin);
        
        GC.SuppressFinalize(this);
    } // Dispose
} // AccessDBContentReader

Implementierung eines Content Writers

Um Inhalte zu einem Element zu schreiben, muss ein Anbieter eine Content Writer-Klasse implementieren, die von System.Management.Automation.Provider.IContentWriter abgeleitet ist. Die Content Writer-Klasse definiert eine Write-Methode , die den angegebenen Zeileninhalt schreibt, eine Seek-Methode , die den Content Writer bewegt, eine Close-Methode , die den Content Writer schließt, und eine Dispose-Methode .

public class AccessDBContentWriter : IContentWriter
{
    // A provider instance is required so as to get "content"
    private AccessDBProvider provider;
    private string path;
    private long currentOffset;

    internal AccessDBContentWriter(string path, AccessDBProvider provider)
    {
        this.path = path;
        this.provider = provider;
    }

    /// <summary>
    /// Write the specified row contents in the source
    /// </summary>
    /// <param name="content"> The contents to be written to the source.
    /// </param>
    /// <returns>An array of elements which were successfully written to 
    /// the source</returns>
    /// 
    public IList Write(IList content)
    {
        if (content == null)
        {
            return null;
        }

        // Get the total number of rows currently available it will 
        // determine how much to overwrite and how much to append at
        // the end
        string tableName;
        int rowNumber;
        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        if (type == PathType.Table)
        {
            OdbcDataAdapter da = provider.GetAdapterForTable(tableName);
            if (da == null)
            {
                return null;
            }

            DataSet ds = provider.GetDataSetForTable(da, tableName);
            DataTable table = provider.GetDataTable(ds, tableName);

            string[] colValues = (content[0] as string).Split(',');

            // set the specified row
            DataRow row = table.NewRow();

            for (int i = 0; i < colValues.Length; i++)
            {
                if (!String.IsNullOrEmpty(colValues[i]))
                {
                    row[i] = colValues[i];
                }
            }

            //table.Rows.InsertAt(row, rowNumber);
            // Update the table
            table.Rows.Add(row);
            da.Update(ds, tableName);
            
        }
        else 
        {
            throw new InvalidOperationException("Operation not supported. Content can be added only for tables");
        }

        return null;
    } // Write

    /// <summary>
    /// Moves the content reader specified number of rows from the 
    /// origin
    /// </summary>
    /// <param name="offset">Number of rows to offset</param>
    /// <param name="origin">Starting row from which to offset</param>
    public void Seek(long offset, System.IO.SeekOrigin origin)
    {
        // get the number of rows in the table which will help in
        // calculating current position
        string tableName;
        int rowNumber;

        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        if (type == PathType.Invalid)
        {
            throw new ArgumentException("Path specified should represent either a table or a row : " + path);
        }

        Collection<DatabaseRowInfo> rows =
               provider.GetRows(tableName);

        int numRows = rows.Count;

        if (offset > rows.Count)
        {
            throw new
                   ArgumentException(
                       "Offset cannot be greater than the number of rows available"
                                           );
        }

        if (origin == System.IO.SeekOrigin.Begin)
        {
            // starting from Beginning with an index 0, the current offset
            // has to be advanced to offset - 1
            currentOffset = offset - 1;
        }
        else if (origin == System.IO.SeekOrigin.End)
        {
            // starting from the end which is numRows - 1, the current
            // offset is so much less than numRows - 1
            currentOffset = numRows - 1 - offset;
        }
        else
        {
            // calculate from the previous value of current offset
            // advancing forward always
            currentOffset += offset;
        }

    } // Seek

    /// <summary>
    /// Closes the content reader, so all members are reset
    /// </summary>
    public void Close()
    {
        Dispose();
    } // Close

    /// <summary>
    /// Dispose any resources being used
    /// </summary>
    public void Dispose()
    {
        Seek(0, System.IO.SeekOrigin.Begin);

        GC.SuppressFinalize(this);
    } // Dispose
} // AccessDBContentWriter

Abruf des Inhaltslesers

Um Inhalte von einem Element zu erhalten, muss der Anbieter das System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* implementieren, um das Get-Content Cmdlet zu unterstützen. Diese Methode gibt den Inhaltsleser für das auf dem angegebene Pfad liegende Element zurück. Das Reader-Objekt kann dann geöffnet werden, um den Inhalt zu lesen.

Hier ist die Implementierung von System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* für diese Methode für diesen Anbieter.

public IContentReader GetContentReader(string path)
{
    string tableName;
    int rowNumber;

    PathType type = GetNamesFromPath(path, out tableName, out rowNumber);

    if (type == PathType.Invalid)
    {
        ThrowTerminatingInvalidPathException(path);
    }
    else if (type == PathType.Row)
    {
        throw new InvalidOperationException("contents can be obtained only for tables");
    }

    return new AccessDBContentReader(path, this);
} // GetContentReader
public IContentReader GetContentReader(string path)
{
    string tableName;
    int rowNumber;

    PathType type = GetNamesFromPath(path, out tableName, out rowNumber);

    if (type == PathType.Invalid)
    {
        ThrowTerminatingInvalidPathException(path);
    }
    else if (type == PathType.Row)
    {
        throw new InvalidOperationException("contents can be obtained only for tables");
    }

    return new AccessDBContentReader(path, this);
} // GetContentReader

Dinge, die Sie bei der Implementierung von GetContentReader beachten sollten

Die folgenden Bedingungen können für eine Implementierung von System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* gelten:

Dynamische Parameter an das Get-Content Cmdlet anhängen

Das Get-Content Cmdlet kann zusätzliche Parameter benötigen, die zur Laufzeit dynamisch festgelegt werden. Um diese dynamischen Parameter bereitzustellen, muss der Windows-PowerShell-Inhaltsanbieter die Methode System.Management.Automation.Provider.IContentCmdletProvider.GetContentReaderdynamicparameters* implementieren. Diese Methode ruft dynamische Parameter für das Element auf dem angegebenen Pfad ab und gibt ein Objekt zurück, das Eigenschaften und Felder mit Parsing-Attributen besitzt, ähnlich einer cmdlet-Klasse oder einem System.Management.Automation.RuntimeDefinedParameterDictionary-Objekt . Die Windows-PowerShell-Laufzeit verwendet das zurückgegebene Objekt, um die Parameter zum Cmdlet hinzuzufügen.

Dieser Windows-PowerShell-Container-Anbieter implementiert diese Methode nicht. Der folgende Code ist jedoch die Standardimplementierung dieser Methode.

public object GetContentReaderDynamicParameters(string path)
{
    return null;
}
public object GetContentReaderDynamicParameters(string path)
{
    return null;
}

Den Content Writer abrufen

Um Inhalte zu einem Element zu schreiben, muss der Anbieter das System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* implementieren, um die Set-Content Cmdlets Add-Content zu unterstützen. Diese Methode gibt den Content Writer für das am angegebenen Pfad liegende Element zurück.

Hier ist die Implementierung von System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* für diese Methode.

public IContentWriter GetContentWriter(string path)
{
    string tableName;
    int rowNumber;

    PathType type = GetNamesFromPath(path, out tableName, out rowNumber);

    if (type == PathType.Invalid)
    {
        ThrowTerminatingInvalidPathException(path);
    }
    else if (type == PathType.Row)
    {
        throw new InvalidOperationException("contents can be added only to tables");
    }

    return new AccessDBContentWriter(path, this);
}
public IContentWriter GetContentWriter(string path)
{
    string tableName;
    int rowNumber;

    PathType type = GetNamesFromPath(path, out tableName, out rowNumber);

    if (type == PathType.Invalid)
    {
        ThrowTerminatingInvalidPathException(path);
    }
    else if (type == PathType.Row)
    {
        throw new InvalidOperationException("contents can be added only to tables");
    }

    return new AccessDBContentWriter(path, this);
}

Was Sie bei der Implementierung von GetContentWriter beachten sollten

Die folgenden Bedingungen können für Ihre Implementierung von System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* gelten:

Dynamische Parameter an die Add-Content und Set-Content Cmdlets anhängen

Die und Set-Content Cmdlets Add-Content benötigen möglicherweise zusätzliche dynamische Parameter, die zur Laufzeit hinzugefügt werden. Um diese dynamischen Parameter bereitzustellen, muss der Windows-PowerShell-Inhaltsanbieter die Methode System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriterDynamicParameters* implementieren, um diese Parameter zu handhaben. Diese Methode ruft dynamische Parameter für das Element auf dem angegebenen Pfad ab und gibt ein Objekt zurück, das Eigenschaften und Felder mit Parsing-Attributen besitzt, ähnlich einer cmdlet-Klasse oder einem System.Management.Automation.RuntimeDefinedParameterDictionary-Objekt . Die Windows-PowerShell-Laufzeit verwendet das zurückgegebene Objekt, um die Parameter zu den Cmdlets hinzuzufügen.

Dieser Windows-PowerShell-Container-Anbieter implementiert diese Methode nicht. Der folgende Code ist jedoch die Standardimplementierung dieser Methode.

public object GetContentWriterDynamicParameters(string path)
{
    return null;
}

Inhalt löschen

Ihr Inhaltsanbieter implementiert die System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* -Methode zur Unterstützung des Clear-Content Cmdlets. Diese Methode entfernt den Inhalt des Objekts auf dem angegebenen Pfad, lässt das Element jedoch unversehrt.

Hier ist die Implementierung der System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* -Methode für diesen Anbieter.

public void ClearContent(string path)
{
    string tableName;
    int rowNumber;

    PathType type = GetNamesFromPath(path, out tableName, out rowNumber);

    if (type != PathType.Table)
    {
        WriteError(new ErrorRecord(
            new InvalidOperationException("Operation not supported. Content can be cleared only for table"),
                "NotValidRow", ErrorCategory.InvalidArgument,
                    path));
        return;
    }

    OdbcDataAdapter da = GetAdapterForTable(tableName);

    if (da == null)
    {
        return;
    }

    DataSet ds = GetDataSetForTable(da, tableName);
    DataTable table = GetDataTable(ds, tableName);

    // Clear contents at the specified location
    for (int i = 0; i < table.Rows.Count; i++)
    {
        table.Rows[i].Delete();
    }

    if (ShouldProcess(path, "ClearContent"))
    {
        da.Update(ds, tableName);
    }

} // ClearContent

Was Sie bei der Implementierung von ClearContent beachten sollten

Die folgenden Bedingungen können für eine Implementierung von System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* gelten:

Dynamische Parameter an das Clear-Content Cmdlet anhängen

Das Clear-Content CMDLET könnte zusätzliche dynamische Parameter benötigen, die zur Laufzeit hinzugefügt werden. Um diese dynamischen Parameter bereitzustellen, muss der Windows-PowerShell-Inhaltsanbieter die Methode System.Management.Automation.Provider.IContentCmdletProvider.ClearContentDynamicParameters* implementieren, um diese Parameter zu handhaben. Diese Methode ruft die Parameter für das Element auf dem angegebenen Pfad ab. Diese Methode ruft dynamische Parameter für das Element auf dem angegebenen Pfad ab und gibt ein Objekt zurück, das Eigenschaften und Felder mit Parsing-Attributen besitzt, ähnlich einer cmdlet-Klasse oder einem System.Management.Automation.RuntimeDefinedParameterDictionary-Objekt . Die Windows-PowerShell-Laufzeit verwendet das zurückgegebene Objekt, um die Parameter zum Cmdlet hinzuzufügen.

Dieser Windows-PowerShell-Container-Anbieter implementiert diese Methode nicht. Der folgende Code ist jedoch die Standardimplementierung dieser Methode.

public object ClearContentDynamicParameters(string path)
{
    return null;
}
public object ClearContentDynamicParameters(string path)
{
    return null;
}

Codebeispiel

Für den vollständigen Beispielcode siehe AccessDbProviderSample06 Code Example.

Definition von Objekttypen und Formatierung

Beim Schreiben eines Providers kann es notwendig sein, bestehenden Objekten Mitglieder hinzuzufügen oder neue Objekte zu definieren. Wenn dies geschieht, müssen Sie eine Types-Datei erstellen, die Windows PowerShell verwenden kann, um die Mitglieder des Objekts zu identifizieren, sowie eine Format-Datei, die definiert, wie das Objekt angezeigt wird. Weitere Informationen finden Sie unter Erweiterung von Objekttypen und Formatierung.

Aufbau des Windows PowerShell Providers

Sieh dir an, wie man Cmdlets, Anbieter und Host-Anwendungen registriert.

Testen des Windows PowerShell-Anbieters

Wenn dein Windows-PowerShell-Anbieter bei Windows PowerShell registriert ist, kannst du das testen, indem du die unterstützten Kommandozeilen auf der Kommandozeile ausführst. Testen Sie zum Beispiel den Sample-Content-Anbieter.

Verwenden Sie das Get-Content cmdlet, um den Inhalt des angegebenen Elements in der Datenbanktabelle auf dem vom Parameter Path angegebenen Pfad abzurufen. Der Parameter ReadCount gibt die Anzahl der Elemente an, die der definierte Inhaltsleser lesen soll (Standard 1). Mit dem folgenden Befehlseintrag holt das Cmdlet zwei Zeilen (Items) aus der Tabelle und zeigt deren Inhalt an. Beachten Sie, dass die folgende Beispielausgabe eine fiktive Access-Datenbank verwendet.

Get-Content -Path mydb:\Customers -ReadCount 2
ID        : 1
FirstName : Eric
LastName  : Gruber
Email     : ericgruber@fabrikam.com
Title     : President
Company   : Fabrikam
WorkPhone : (425) 555-0100
Address   : 4567 Main Street
City      : Buffalo
State     : NY
Zip       : 98052
Country   : USA
ID        : 2
FirstName : Eva
LastName  : Corets
Email     : evacorets@cohowinery.com
Title     : Sales Representative
Company   : Coho Winery
WorkPhone : (360) 555-0100
Address   : 8910 Main Street
City      : Cabmerlot
State     : WA
Zip       : 98089
Country   : USA

Siehe auch

Erstellung von Windows-PowerShell-Anbietern

Design Your Windows PowerShell Provider

Erweiterung von Objekttypen und Formatierung

Implementiere einen Navigations-Windows-PowerShell-Anbieter

Wie man Cmdlets, Anbieter und Host-Anwendungen registriert

Windows PowerShell SDK

Windows PowerShell Programmer's Guide