Skapa en Windows PowerShell-innehållsleverantör

Detta ämne beskriver hur man skapar en Windows PowerShell-leverantör som gör det möjligt för användaren att manipulera innehållet i objekten i en datalagring. Som en följd kallas en leverantör som kan manipulera innehållet i objekt för en Windows PowerShell-innehållsleverantör.

Anmärkning

Du kan ladda ner C#-källfilen (AccessDBSampleProvider06.cs) för denna leverantör med hjälp av Microsoft Windows Software Development Kit för Windows Vista och .NET Framework 3.0 Runtime Components. För nedladdningsinstruktioner, se Hur man installerar Windows PowerShell och laddar ner Windows PowerShell SDK. De nedladdade källfilerna finns tillgängliga i <PowerShell Samples-katalogen> . För mer information om andra implementeringar av Windows PowerShell-leverantörer, se Designing Your Windows PowerShell Provider.

Definiera Windows PowerShell Content Provider-klassen

En Windows PowerShell-innehållsleverantör måste skapa en .NET-klass som stöder System.Management.Automation.Provider.IContentCmdletProvider-gränssnittet . Här är klassdefinitionen för den föremålsleverantör som beskrivs i detta avsnitt.

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

Observera att i denna klassdefinition innehåller attributet System.Management.Automation.Provider.CmdletProviderAttribute två parametrar. Den första parametern specificerar ett användarvänligt namn för leverantören som används av Windows PowerShell. Den andra parametern specificerar de Windows PowerShell-specifika funktioner som leverantören exponerar för Windows PowerShell-runtime under kommandobearbetningen. För denna leverantör finns inga extra Windows PowerShell-specifika funktioner.

Definiera funktionaliteten hos basklassen

Som beskrivs i Design Your Windows PowerShell Provider härstammar klassen System.Management.Automation.Provider.NavigationCmdletProvider från flera andra klasser som erbjöd olika leverantörsfunktionalitet. En innehållsleverantör för Windows PowerShell definierar därför vanligtvis all funktionalitet som tillhandahålls av dessa klasser.

För mer information om hur man implementerar funktionalitet för att lägga till sessionsspecifik initieringsinformation och för att släppa resurser som används av leverantören, se Skapa en grundläggande Windows PowerShell-leverantör. De flesta leverantörer, inklusive den som beskrivs här, kan dock använda standardimplementeringen av denna funktionalitet som tillhandahålls av Windows PowerShell.

För att komma åt datalagringen måste leverantören implementera metoderna i basklassen System.Management.Automation.Provider.DriveCmdletProvider . För mer information om hur man implementerar dessa metoder, se Skapa en Windows PowerShell Drive Provider.

För att manipulera objekten i en datalagring, såsom att hämta, sätta och rensa objekt, måste leverantören implementera metoderna som tillhandahålls av Basklassen System.Management.Automation.Provider.ItemCmdletProvider . För mer information om hur man implementerar dessa metoder, se Skapa en Windows PowerShell Item Provider.

För att arbeta med flerskiktsdatalager måste leverantören implementera metoderna som tillhandahålls av Basklassen System.Management.Automation.Provider.ContainerCmdletProvider . För mer information om hur man implementerar dessa metoder, se Skapa en Windows PowerShell Container Provider.

För att stödja rekursiva kommandon, nästlade containrar och relativa vägar måste leverantören implementera basklassen System.Management.Automation.Provider.NavigationCmdletProvider . Dessutom kan denna Windows PowerShell-innehållsleverantör koppla System.Management.Automation.Provider.IContentCmdletProvider-gränssnittet till basklassen System.Management.Automation.Provider.NavigationCmdletProvider , och måste därför implementera de metoder som tillhandahålls av den klassen. För mer information, se implementera dessa metoder, se Implementera en navigationsleverantör för Windows PowerShell.

Implementering av en innehållsläsare

För att läsa innehåll från ett objekt måste en leverantör implementera en innehållsläsarklass som härstammar från System.Management.Automation.Provider.IContentReader. Innehållsläsaren för denna leverantör ger tillgång till innehållet i en rad i en datatabell. Innehållsläsarklassen definierar en Läsmetod som hämtar data från den angivna raden och returnerar en lista som representerar den datan, en Seek-metod som flyttar innehållsläsaren, en Close-metod som stänger innehållsläsaren och en Dispose-metod .

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

Att implementera en innehållsförfattare

För att skriva innehåll till ett objekt måste en leverantör implementera en content writer-klass som härstammar från System.Management.Automation.Provider.IContentWriter. Content writer-klassen definierar en Write-metod som skriver det angivna radinnehållet, en Seek-metod som flyttar innehållsförfattaren, en Close-metod som stänger innehållsskrivaren och en Dispose-metod .

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

Hämta innehållsläsaren

För att få innehåll från ett objekt måste leverantören implementera System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* för att stödja Get-Content cmdleten. Denna metod returnerar innehållsläsaren för objektet som finns på den angivna sökvägen. Läsarobjektet kan sedan öppnas för att läsa innehållet.

Här är implementationen av System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* för denna metod för denna leverantör.

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

Saker att komma ihåg när man implementerar GetContentReader

Följande villkor kan gälla för en implementation av System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader*:

Att fästa dynamiska parametrar till Get-Content cmdleten

Cmdleten Get-Content kan kräva ytterligare parametrar som specificeras dynamiskt vid körning. För att tillhandahålla dessa dynamiska parametrar måste Windows PowerShell-innehållsleverantören implementera System.Management.Automation.Provider.IContentCmdletProvider.GetContentReaderdynamicparameters* -metoden. Denna metod hämtar dynamiska parametrar för objektet vid den angivna sökvägen och returnerar ett objekt som har egenskaper och fält med parsingattribut liknande en cmdlet-klass eller ett System.Management.Automation.RuntimeDefinedParameterDictionary-objekt . Windows PowerShell-runtime använder det returnerade objektet för att lägga till parametrarna i cmdleten.

Denna Windows PowerShell-containerleverantör implementerar inte denna metod. Följande kod är dock standardimplementeringen av denna metod.

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

Hämta innehållsskrivaren

För att skriva innehåll till ett objekt måste leverantören implementera System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* för att stödja Set-Content och Add-Content cmdlets. Denna metod returnerar innehållsskrivaren för objektet som finns på den angivna vägen.

Här är implementeringen av System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* för denna metod.

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

Saker att komma ihåg när du implementerar GetContentWriter

Följande villkor kan gälla för din implementation av System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter*:

Att koppla dynamiska parametrar till Add-Content- och Set-Content-cmdlets

Och Set-Content cmdlets Add-Content kan kräva ytterligare dynamiska parametrar som läggs till under körtiden. För att tillhandahålla dessa dynamiska parametrar måste Windows PowerShell-innehållsleverantören implementera metoden System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriterDynamicParameters* för att hantera dessa parametrar. Denna metod hämtar dynamiska parametrar för objektet vid den angivna sökvägen och returnerar ett objekt som har egenskaper och fält med parsingattribut liknande en cmdlet-klass eller ett System.Management.Automation.RuntimeDefinedParameterDictionary-objekt . Windows PowerShell-runtime använder det returnerade objektet för att lägga till parametrarna i cmdlets.

Denna Windows PowerShell-containerleverantör implementerar inte denna metod. Följande kod är dock standardimplementeringen av denna metod.

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

Rensa innehåll

Din innehållsleverantör implementerar System.Management.Automation.Provider.IContentCmdletProvider.ClearContent*- metoden för att stödja Clear-Content cmdleten. Denna metod tar bort innehållet i objektet på den angivna vägen, men lämnar objektet intakt.

Här är implementeringen av System.Management.Automation.Provider.IContentCmdletProvider.ClearContent*- metoden för denna leverantör.

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

Saker att komma ihåg när man implementerar ClearContent

Följande villkor kan gälla för en implementation av System.Management.Automation.Provider.IContentCmdletProvider.ClearContent*:

Att fästa dynamiska parametrar till Clear-Content cmdleten

Cmdleten Clear-Content kan kräva ytterligare dynamiska parametrar som läggs till vid körning. För att tillhandahålla dessa dynamiska parametrar måste Windows PowerShell-innehållsleverantören implementera metoden System.Management.Automation.Provider.IContentCmdletProvider.ClearContentDynamicParameters* för att hantera dessa parametrar. Denna metod hämtar parametrarna för objektet vid den angivna vägen. Denna metod hämtar dynamiska parametrar för objektet vid den angivna sökvägen och returnerar ett objekt som har egenskaper och fält med parsingattribut liknande en cmdlet-klass eller ett System.Management.Automation.RuntimeDefinedParameterDictionary-objekt . Windows PowerShell-runtime använder det returnerade objektet för att lägga till parametrarna i cmdleten.

Denna Windows PowerShell-containerleverantör implementerar inte denna metod. Följande kod är dock standardimplementeringen av denna metod.

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

Kodexempel

För komplett exempelkod, se AccessDbProviderSample06 Code Sample.

Definiera objekttyper och formatering

När man skriver en leverantör kan det vara nödvändigt att lägga till medlemmar till befintliga objekt eller definiera nya objekt. När detta är gjort måste du skapa en Types-fil som Windows PowerShell kan använda för att identifiera objektets medlemmar och en Format-fil som definierar hur objektet visas. För mer information, se Utökning av objekttyper och formatering.

Bygga Windows PowerShell-leverantören

Se hur du registrerar cmdlets, leverantörer och värdapplikationer.

Testning av Windows PowerShell-leverantören

När din Windows PowerShell-leverantör har registrerats hos Windows PowerShell kan du testa det genom att köra de stödda cmdletarna på kommandoraden. Testa till exempel den exempelbaserade innehållsleverantören.

Använd cmdleten Get-Content för att hämta innehållet i det specificerade objektet i databastabellen på den sökväg som parametern Path angäller. Parametern ReadCount anger antalet objekt som den definierade innehållsläsaren ska läsa (standard 1). Med följande kommandopost hämtar cmdleten två rader (objekt) från tabellen och visar deras innehåll. Observera att följande exempelutdata använder en fiktiv Access-databas.

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

Se även

Skapa Windows PowerShell-leverantörer

Designa din Windows PowerShell-leverantör

Utvidgning av objekttyper och formatering

Implementera en Navigation Windows PowerShell-leverantör

Hur man registrerar cmdlets, leverantörer och värdapplikationer

Windows PowerShell SDK

Windows PowerShell Programmerarguide