Dela via


Skapa en Windows PowerShell-innehållsprovider

Det här avsnittet beskriver hur du skapar en Windows PowerShell-provider som gör det möjligt för användaren att ändra innehållet i objekten i ett datalager. Därför kallas en provider som kan ändra innehållet i objekt som en Windows PowerShell-innehållsprovider.

Anmärkning

Du kan ladda ned C#-källfilen (AccessDBSampleProvider06.cs) för den här providern med hjälp av Microsoft Windows Software Development Kit för Windows Vista och .NET Framework 3.0 Runtime Components. Instruktioner för nedladdning finns i Så här installerar du Windows PowerShell och laddar ned Windows PowerShell SDK. De nedladdade källfilerna är tillgängliga i katalogen <PowerShell-exempel>. Mer information om andra Implementeringar av Windows PowerShell-providern finns i Designing Your Windows PowerShell Provider.

Definiera Windows PowerShell-innehållsproviderklassen

En Windows PowerShell-innehållsprovider måste skapa en .NET-klass som stöder gränssnittet System.Management.Automation.Provider.IContentCmdletProvider. Här är klassdefinitionen för objektprovidern som beskrivs i det här avsnittet.

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

Observera att attributet System.Management.Automation.Provider.CmdletProviderAttribute innehåller två parametrar i den här klassdefinitionen. Den första parametern anger ett användarvänligt namn för providern som används av Windows PowerShell. Den andra parametern anger de Windows PowerShell-specifika funktioner som providern exponerar för Windows PowerShell-körningen under kommandobearbetningen. För den här providern finns det inga tillagda Windows PowerShell-specifika funktioner.

Definiera funktioner i basklassen

Enligt beskrivningen i Design Your Windows PowerShell Providerhärleds klassen System.Management.Automation.Provider.NavigationCmdletProvider från flera andra klasser som tillhandahåller olika providerfunktioner. En Windows PowerShell-innehållsprovider definierar därför vanligtvis alla funktioner som tillhandahålls av dessa klasser.

Mer information om hur du implementerar funktioner för att lägga till sessionsspecifik initieringsinformation och för att frigöra resurser som används av providern finns i Skapa en grundläggande Windows PowerShell-provider. De flesta leverantörer, inklusive providern som beskrivs här, kan dock använda standardimplementeringen av den här funktionen som tillhandahålls av Windows PowerShell.

För att få åtkomst till datalagret måste providern implementera metoderna i basklassen System.Management.Automation.Provider.DriveCmdletProvider. Mer information om hur du implementerar dessa metoder finns i Skapa en Windows PowerShell-enhetsprovider.

Om du vill ändra objekten i ett datalager, till exempel hämta, ange och rensa objekt, måste providern implementera de metoder som tillhandahålls av System.Management.Automation.Provider.ItemCmdletProvider basklass. Mer information om hur du implementerar dessa metoder finns i Skapa en Windows PowerShell-objektprovider.

Om du vill arbeta med datalager i flera lager måste providern implementera de metoder som tillhandahålls av System.Management.Automation.Provider.ContainerCmdletProvider basklass. Mer information om hur du implementerar dessa metoder finns i Skapa en Windows PowerShell-containerprovider.

För att stödja rekursiva kommandon, kapslade containrar och relativa sökvägar måste providern implementera System.Management.Automation.Provider.NavigationCmdletProvider basklass. Dessutom kan den här Windows PowerShell-innehållsleverantören koppla System.Management.Automation.Provider.IContentCmdletProvider-gränssnittet till System.Management.Automation.Provider.NavigationCmdletProvider basklass och måste därför implementera metoderna som tillhandahålls av den klassen. Mer information finns i implementera dessa metoder i Implementera en Windows PowerShell-provider för navigering.

Implementera en innehållsläsare

Om du vill läsa innehåll från ett objekt måste en provider implementera en innehållsläsarklass som härleds från System.Management.Automation.Provider.IContentReader. Innehållsläsaren för den här providern ger åtkomst till innehållet i en rad i en datatabell. Innehållsläsarklassen definierar en Read-metod som hämtar data från den angivna raden och returnerar en lista som representerar dessa data, en Sök-metod som flyttar innehållsläsaren, en metod Stäng 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

Implementera en innehållsskrivare

Om du vill skriva innehåll till ett objekt måste en provider implementera en innehållsskrivarklass som härleds från System.Management.Automation.Provider.IContentWriter. Klassen content writer 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ållsförfattaren 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 hämta innehåll från ett objekt måste providern implementera System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* för att stödja cmdleten Get-Content. Den här metoden 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 implementeringen av System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* för den här metoden för den här providern.

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 om att implementera GetContentReader

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

Koppla dynamiska parametrar till cmdleten Get-Content

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

Den här Windows PowerShell-containerprovidern implementerar inte den här metoden. Följande kod är dock standardimplementeringen av den här metoden.

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

Hämtar innehållsförfattaren

Om du vill skriva innehåll till ett objekt måste providern implementera System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* för att stödja cmdletarna Set-Content och Add-Content. Den här metoden returnerar innehållsskrivaren för objektet som finns på den angivna sökvägen.

Här är implementeringen av System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* för den här metoden.

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 om att implementera GetContentWriter

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

Koppla dynamiska parametrar till cmdletarna Add-Content och Set-Content

Cmdletarna Add-Content och Set-Content kan kräva ytterligare dynamiska parametrar som läggs till en körning. För att tillhandahålla dessa dynamiska parametrar måste Windows PowerShell-innehållsprovidern implementera System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriterDynamicParameters* metod för att hantera dessa parametrar. Den här metoden hämtar dynamiska parametrar för objektet på den angivna sökvägen och returnerar ett objekt som har egenskaper och fält med parsningsattribut som liknar en cmdlet-klass eller en System.Management.Automation.RuntimeDefinedParameterDictionary-objekt. Windows PowerShell-körningen använder det returnerade objektet för att lägga till parametrarna i cmdletarna.

Den här Windows PowerShell-containerprovidern implementerar inte den här metoden. Följande kod är dock standardimplementeringen av den här metoden.

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

Rensa innehåll

Innehållsleverantören implementerar metoden System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* till stöd för cmdleten Clear-Content. Den här metoden tar bort innehållet i objektet på den angivna sökvägen, men lämnar objektet intakt.

Här är implementeringen av metoden System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* för den här providern.

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 om att implementera ClearContent

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

Koppla dynamiska parametrar till cmdleten Clear-Content

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ållsprovidern implementera metoden System.Management.Automation.Provider.IContentCmdletProvider.ClearContentDynamicParameters* för att hantera dessa parametrar. Den här metoden hämtar parametrarna för objektet på den angivna sökvägen. Den här metoden hämtar dynamiska parametrar för objektet på den angivna sökvägen och returnerar ett objekt som har egenskaper och fält med parsningsattribut som liknar en cmdlet-klass eller en System.Management.Automation.RuntimeDefinedParameterDictionary-objekt. Windows PowerShell-körningen använder det returnerade objektet för att lägga till parametrarna i cmdleten.

Den här Windows PowerShell-containerprovidern implementerar inte den här metoden. Följande kod är dock standardimplementeringen av den här metoden.

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

Kodexempel

Fullständig exempelkod finns i AccessDbProviderSample06 Code Sample.

Definiera objekttyper och formatering

När du skriver en provider kan det vara nödvändigt att lägga till medlemmar i befintliga objekt eller definiera nya objekt. När detta är klart måste du skapa en typfil som Windows PowerShell kan använda för att identifiera medlemmarna i objektet och en formatfil som definierar hur objektet visas. Mer information finns i Utöka objekttyper och formatering.

Skapa Windows PowerShell-providern

Se Så här registrerar du cmdletar, leverantörer och värdprogram.

Testa Windows PowerShell-providern

När din Windows PowerShell-provider har registrerats med Windows PowerShell kan du testa den genom att köra de cmdletar som stöds på kommandoraden. Du kan till exempel testa exempelinnehållsprovidern.

Använd cmdleten Get-Content för att hämta innehållet i det angivna objektet i databastabellen på sökvägen som anges av parametern Path. 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 innehållet. 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-providers

Utforma Din Windows PowerShell-provider

utöka objekttyper och formatering

Implementera en Windows PowerShell-provider för navigering

Registrera cmdletar, leverantörer och värdprogram

Windows PowerShell SDK

Windows PowerShell-programmerarens guide