Tworzenie dostawcy zawartości programu Windows PowerShell

W tym temacie opisano sposób tworzenia dostawcy Windows PowerShell, który umożliwia użytkownikowi manipulowanie zawartością elementów w magazynie danych. W związku z tym dostawca, który może manipulować zawartością elementów, jest określany jako Windows PowerShell zawartości.

Uwaga

Możesz pobrać plik źródłowy języka C# (AccessDBSampleProvider06.cs) dla tego dostawcy przy użyciu zestawu Microsoft Windows Software Development Kit dla składników środowiska uruchomieniowego Windows Vista i .NET Framework 3.0. Aby uzyskać instrukcje dotyczące pobierania, zobacz How to Install Windows PowerShell and Download the Windows PowerShell SDK (Jak zainstalować zestaw SDK usługi Windows PowerShell i pobrać Windows PowerShell SDK). Pobrane pliki źródłowe są dostępne w <PowerShell Samples> katalogu . Aby uzyskać więcej informacji na temat Windows PowerShell implementacji innych dostawców, zobacz Projektowanie dostawcy Windows PowerShell danych.

Definiowanie Windows PowerShell dostawcy zawartości

Dostawca Windows PowerShell musi utworzyć klasę .NET, która obsługuje interfejs System.Management.Automation.Provider.Icontentcmdletprovider. Oto definicja klasy dostawcy elementów opisana w tej sekcji.

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

Należy pamiętać, że w tej definicji klasy atrybut System.Management.Automation.Provider.Cmdletproviderattribute zawiera dwa parametry. Pierwszy parametr określa przyjazną dla użytkownika nazwę dostawcy, która jest używana przez Windows PowerShell. Drugi parametr określa Windows PowerShell możliwości, które dostawca uwidacznia w środowisku uruchomieniowym Windows PowerShell podczas przetwarzania poleceń. Dla tego dostawcy nie ma żadnych dodanych Windows PowerShell określonych możliwości.

Definiowanie funkcjonalności klasy bazowej

Zgodnie z opisem w te Windows PowerShell,klasa System.Management.Automation.Provider.Navigationcmdletprovider pochodzi z kilku innych klas, które zapewniały różne funkcje dostawcy. Dlatego Windows PowerShell dostawcy zawartości zwykle definiuje wszystkie funkcje udostępniane przez te klasy.

Aby uzyskać więcej informacji na temat sposobu implementowania funkcji dodawania informacji o inicjowaniu specyficznym dla sesji i wydawania zasobów używanych przez dostawcę, zobacz Tworzenie podstawowego dostawcy Windows PowerShell sesji. Jednak większość dostawców, w tym dostawca opisany tutaj, może używać domyślnej implementacji tej funkcji, która jest zapewniana przez Windows PowerShell.

Aby uzyskać dostęp do magazynu danych, dostawca musi zaimplementować metody klasy bazowej System.Management.Automation.Provider.Drivecmdletprovider. Aby uzyskać więcej informacji na temat implementowania tych metod, zobacz Creating a Windows PowerShell Drive Provider.

Aby manipulować elementami magazynu danych, takimi jak pobieranie, ustawianie i czyszczenie elementów, dostawca musi zaimplementować metody dostarczane przez klasę bazową System.Management.Automation.Provider.Itemcmdletprovider. Aby uzyskać więcej informacji na temat implementowania tych metod, zobacz Creating a Windows PowerShell Item Provider (Tworzenie dostawcy Windows PowerShell elementów).

Aby można było pracować z wielowarstwowymi magazynami danych, dostawca musi zaimplementować metody dostarczane przez klasę bazową System.Management.Automation.Provider.Containercmdletprovider. Aby uzyskać więcej informacji na temat implementowania tych metod, zobacz Creating a Windows PowerShell Container Provider (Tworzenie Windows PowerShell kontenera).

Aby obsługiwać polecenia cykliczne, zagnieżdżone kontenery i ścieżki względne, dostawca musi zaimplementować klasę bazową System.Management.Automation.Provider.Navigationcmdletprovider. Ponadto ten dostawca zawartości Windows PowerShell może dołączać interfejs System.Management.Automation.Provider.Icontentcmdletprovider do klasy bazowej System.Management.Automation.Provider.Navigationcmdletprovider i w związku z tym musi implementować metody dostarczane przez tę klasę. Aby uzyskać więcej informacji, zobacz implementowanie tych metod, zobacz Implementowanie dostawcy Windows PowerShell nawigacji.

Implementowanie czytnika zawartości

Aby odczytać zawartość z elementu, dostawca musi zaimplementować klasę czytnika zawartości pochodzącą z klasy System.Management.Automation.Provider.Icontentreader. Czytnik zawartości dla tego dostawcy umożliwia dostęp do zawartości wiersza w tabeli danych. Klasa czytnika zawartości definiuje metodę Read, która pobiera dane ze wskazanego wiersza i zwraca listę reprezentującą te dane, metodę Seek, która przenosi czytnik zawartości, metodę Close, która zamyka czytnik zawartości, oraz metodę Dispose.

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

Implementowanie autorzy zawartości

Aby zapisać zawartość do elementu, dostawca musi zaimplementować klasę elementu zapisującego zawartość pochodzącą z klasy System.Management.Automation.Provider.Icontentwriter. Klasa zapisu zawartości definiuje metodę Write, która zapisuje zawartość określonego wiersza, metodę Seek, która przenosi zapis zawartości, metodę Close, która zamyka autora zawartości, oraz metodę Dispose.

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

Pobieranie czytnika zawartości

Aby uzyskać zawartość z elementu, dostawca musi zaimplementować element System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentreader* w celu obsługi Get-Content polecenia cmdlet . Ta metoda zwraca czytnik zawartości dla elementu znajdującego się w określonej ścieżce. Następnie można otworzyć obiekt czytnika, aby odczytać zawartość.

Oto implementacja metody System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentreader* dla tej metody dla tego dostawcy.

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

Co należy zapamiętać na temat implementowania getContentReader

Następujące warunki mogą dotyczyć implementacji narzędzia System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentreader*:

Dołączanie parametrów dynamicznych do Get-Content cmdlet

Polecenie Get-Content cmdlet może wymagać dodatkowych parametrów, które są określane dynamicznie w czasie wykonywania. Aby zapewnić te parametry dynamiczne, dostawca zawartości Windows PowerShell musi zaimplementować metodę System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentreaderdynamicparameters*. Ta metoda pobiera parametry dynamiczne dla elementu we wskazanej ścieżce i zwraca obiekt, który ma właściwości i pola z atrybutami analizowania podobnymi do klasy polecenia cmdlet lub obiektu System.Management.Automation.Runtimedefinedparameterdictionary. Środowisko Windows PowerShell używa zwróconego obiektu w celu dodania parametrów do polecenia cmdlet .

Ten Windows PowerShell kontenera nie implementuje tej metody. Jednak poniższy kod jest domyślną implementacją tej metody.

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

Pobieranie zapisu zawartości

Aby zapisać zawartość do elementu, dostawca musi zaimplementować polecenia cmdlet System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentwriter*. Set-Content Add-Content Ta metoda zwraca element zapisujący zawartość dla elementu znajdującego się w określonej ścieżce.

Oto implementacja metody System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentwriter*.

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

Co należy zapamiętać na temat implementowania GetContentWriter

Następujące warunki mogą dotyczyć Implementacja System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentwriter*:

Dołączanie parametrów dynamicznych do Add-Content i Set-Content cmdlet

Polecenia Add-Content Set-Content cmdlet i mogą wymagać dodatkowych parametrów dynamicznych, które są dodawane do środowiska uruchomieniowego. Aby zapewnić te parametry dynamiczne, dostawca zawartości Windows PowerShell musi zaimplementować metodę System.Management.Automation.Provider.Icontentcmdletprovider.Getcontentwriterdynamicparameters* w celu obsługi tych parametrów. Ta metoda pobiera parametry dynamiczne dla elementu we wskazanej ścieżce i zwraca obiekt, który ma właściwości i pola z atrybutami analizowania podobnymi do klasy polecenia cmdlet lub obiektu System.Management.Automation.Runtimedefinedparameterdictionary. Środowisko Windows PowerShell używa zwróconego obiektu w celu dodania parametrów do polecenia cmdlet.

Ten Windows PowerShell kontenera nie implementuje tej metody. Jednak poniższy kod jest domyślną implementacją tej metody.

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

Czyszczenie zawartości

Dostawca zawartości implementuje metodę System.Management.Automation.Provider.Icontentcmdletprovider.Clearcontent* w celu obsługi Clear-Content polecenia cmdlet. Ta metoda usuwa zawartość elementu w określonej ścieżce, ale pozostawia element bez zmian.

Oto implementacja metody System.Management.Automation.Provider.Icontentcmdletprovider.Clearcontent* dla tego dostawcy.

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

Co należy zapamiętać na temat implementowania clearContent

Następujące warunki mogą dotyczyć implementacji interfejsu System.Management.Automation.Provider.Icontentcmdletprovider.Clearcontent*:

Dołączanie parametrów dynamicznych do Clear-Content cmdlet

Polecenie Clear-Content cmdlet może wymagać dodatkowych parametrów dynamicznych, które są dodawane w czasie wykonywania. Aby zapewnić te parametry dynamiczne, dostawca zawartości Windows PowerShell musi zaimplementować metodę System.Management.Automation.Provider.Icontentcmdletprovider.Clearcontentdynamicparameters* w celu obsługi tych parametrów. Ta metoda pobiera parametry elementu we wskazanej ścieżce. Ta metoda pobiera parametry dynamiczne dla elementu we wskazanej ścieżce i zwraca obiekt, który ma właściwości i pola z atrybutami analizowania podobnymi do klasy polecenia cmdlet lub obiektu System.Management.Automation.Runtimedefinedparameterdictionary. Środowisko Windows PowerShell używa zwróconego obiektu w celu dodania parametrów do polecenia cmdlet .

Ten Windows PowerShell kontenera nie implementuje tej metody. Jednak poniższy kod jest domyślną implementacją tej metody.

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

Przykład kodu

Aby uzyskać kompletny przykładowy kod, zobacz AccessDbProviderSample06 Code Sample (Przykładowy kod AccessDbProviderSample06).

Definiowanie typów obiektów i formatowania

Podczas pisania dostawcy może być konieczne dodanie elementów członkowskich do istniejących obiektów lub zdefiniowanie nowych obiektów. Gdy to zrobić, należy utworzyć plik typów, który Windows PowerShell służy do identyfikowania elementów członkowskich obiektu i format pliku, który definiuje sposób wyświetlania obiektu. Aby uzyskać więcej informacji, zobacz Rozszerzanie typów obiektów i formatowanie.

Tworzenie dostawcy Windows PowerShell aplikacji

Zobacz How to Register Cmdlets, Providers, and Host Applications (Jak rejestrować polecenia cmdlet, dostawców i aplikacje hosta).

Testowanie dostawcy Windows PowerShell danych

Gdy dostawca Windows PowerShell został zarejestrowany w Windows PowerShell, możesz go przetestować, uruchamiając obsługiwane polecenia cmdlet w wierszu polecenia. Na przykład przetestuj przykładowego dostawcę zawartości.

Użyj polecenia cmdlet , aby pobrać zawartość określonego elementu w tabeli bazy danych w ścieżce Get-Content określonej przez Path parametr . Parametr określa liczbę elementów do odczytania przez ReadCount zdefiniowanego czytnika zawartości (wartość domyślna 1). Za pomocą następującego wpisu polecenia cmdlet pobiera dwa wiersze (elementy) z tabeli i wyświetla ich zawartość. Należy pamiętać, że następujące przykładowe dane wyjściowe wykorzystują fikcyjną bazę danych programu Access.

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

Zobacz też

Tworzenie Windows PowerShell dostawców

Projektowanie dostawcy Windows PowerShell aplikacji

Rozszerzanie typów obiektów i formatowanie

Implementowanie dostawcy Windows PowerShell nawigacji

Jak rejestrować polecenia cmdlet, dostawców i aplikacje hosta

Windows PowerShell SDK

Windows PowerShell — przewodnik programisty