Condividi tramite


Creare un provider di contenuti PowerShell per Windows

Questo argomento descrive come creare un provider PowerShell per Windows che consenta all'utente di manipolare il contenuto degli elementi in un archivio dati. Di conseguenza, un fornitore che può manipolare il contenuto degli elementi è chiamato fornitore di contenuti Windows PowerShell.

Annotazioni

Puoi scaricare il file sorgente C# (AccessDBSampleProvider06.cs) di questo fornitore utilizzando il Microsoft Windows Software Development Kit per Windows Vista e i componenti runtime di .NET Framework 3.0. Per le istruzioni per il download, consulta Come installare Windows PowerShell e scaricare l'SDK di Windows PowerShell. I file sorgente scaricati sono disponibili nella <directory PowerShell Samples> . Per maggiori informazioni su altre implementazioni di provider PowerShell di Windows, vedi Progettare il tuo provider PowerShell Windows.

Definisci la classe di fornitore di contenuti PowerShell di Windows

Un fornitore di contenuti PowerShell per Windows deve creare una classe .NET che supporti l'interfaccia System.Management.Automation.Provider.IContentCmdletProvider . Ecco la definizione di classe per il fornitore di item descritto in questa sezione.

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

Si noti che in questa definizione di classe, l'attributo System.Management.Automation.Provider.CmdletProviderAttribute include due parametri. Il primo parametro specifica un nome user-friendly per il provider utilizzato da Windows PowerShell. Il secondo parametro specifica le capacità specifiche di Windows PowerShell che il provider espone all'runtime di Windows PowerShell durante l'elaborazione dei comandi. Per questo fornitore, non ci sono funzionalità aggiuntive specifiche di PowerShell per Windows.

Definire la funzionalità della classe base

Come descritto in Design Your Windows PowerShell Provider, la classe System.Management.Automation.Provider.NavigationCmdletProvider deriva da diverse altre classi che offrivano funzionalità diverse per i fornitori. Un fornitore di contenuti Windows PowerShell, quindi, tipicamente definisce tutte le funzionalità fornite da queste classi.

Per maggiori informazioni su come implementare funzionalità per aggiungere informazioni di inizializzazione specifiche per la sessione e per rilasciare risorse utilizzate dal provider, vedi Creare un provider base Windows PowerShell. Tuttavia, la maggior parte dei provider, incluso quello descritto qui, può utilizzare l'implementazione predefinita di questa funzionalità fornita da Windows PowerShell.

Per accedere allo store dati, il provider deve implementare i metodi della classe base System.Management.Automation.Provider.DriveCmdletProvider . Per maggiori informazioni sull'implementazione di questi metodi, vedi Creare un provider di unità PowerShell per Windows.

Per manipolare gli elementi di un archivio dati, come ottenere, impostare e cancellare gli elementi, il provider deve implementare i metodi forniti dalla classe base System.Management.Automation.Provider.ItemCmdletProvider . Per maggiori informazioni sull'implementazione di questi metodi, vedi Creare un fornitore di oggetti PowerShell per Windows.

Per lavorare su archivi dati multilivello, il provider deve implementare i metodi forniti dalla classe base System.Management.Automation.Provider.ContainerCmdletProvider . Per maggiori informazioni sull'implementazione di questi metodi, vedi Creare un provider di container Windows PowerShell.

Per supportare comandi ricorsive, container annidati e percorsi relativi, il provider deve implementare la classe base System.Management.Automation.Provider.NavigationCmdletProvider . Inoltre, questo provider di contenuti PowerShell di Windows può collegare l'interfaccia System.Management.Automation.Provider.IContentCmdletProvider alla classe base System.Management.Automation.Provider.NavigationCmdletProvider e deve quindi implementare i metodi forniti da quella classe. Per ulteriori informazioni, vedi implementare questi metodi, vedi Implementa un provider PowerShell per navigazione Windows.

Implementazione di un lettore di contenuti

Per leggere contenuti da un elemento, un fornitore deve implementare una classe di lettore di contenuti che deriva da System.Management.Automation.Provider.IContentReader. Il lettore di contenuti di questo fornitore consente l'accesso al contenuto di una riga in una tabella dati. La classe content reader definisce un metodo Read che recupera i dati dalla riga indicata e restituisce un elenco che rappresenta quei dati, un metodo Seek che muove il content reader, un metodo Close che chiude il content reader e un metodo Dispos .

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

Implementare un Content Writer

Per scrivere contenuti su un elemento, un fornitore deve implementare una classe di content writer derivata da System.Management.Automation.Provider.IContentWriter. La classe content writer definisce un metodo Write che scrive il contenuto specificato della riga, un metodo Seek che muove lo scrittore dei contenuti, un metodo Close che chiude lo scrittore dei contenuti e un metodo Dispos .

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

Recupero del Content Reader

Per ottenere contenuti da un elemento, il fornitore deve implementare il System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* per supportare il Get-Content cmdlet. Questo metodo restituisce il lettore di contenuti dell'elemento situato sul percorso specificato. L'oggetto lettore può quindi essere aperto per leggere il contenuto.

Ecco l'implementazione di System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* per questo metodo per questo fornitore.

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

Cose da ricordare sull'implementazione di GetContentReader

Le seguenti condizioni possono applicarsi a un'implementazione di System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader*:

Attacco dei parametri dinamici al Get-Content cmdlet

Il Get-Content cmdlet potrebbe richiedere parametri aggiuntivi specificati dinamicamente a runtime. Per fornire questi parametri dinamici, il fornitore di contenuti PowerShell di Windows deve implementare il metodo System.Management.Automation.Provider.IContentCmdletProvider.GetContentReaderdynamicparameters* . Questo metodo recupera parametri dinamici per l'elemento sul percorso indicato e restituisce un oggetto che possiede proprietà e campi con attributi di analisi simili a una classe cmdlet o a un oggetto System.Management.Automation.RuntimeDefinedParameterDictionary . L'runtime di Windows PowerShell utilizza l'oggetto restituito per aggiungere i parametri al cmdlet.

Questo provider di container Windows PowerShell non implementa questo metodo. Tuttavia, il seguente codice è l'implementazione predefinita di questo metodo.

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

Recupero del Content Writer

Per scrivere contenuti su un elemento, il provider deve implementare System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* per supportare i Set-Content cmdlet e Add-Content e Questo metodo restituisce il content writer per l'elemento situato sul percorso specificato.

Ecco l'implementazione di System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* per questo metodo.

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

Cose da ricordare sull'implementazione di GetContentWriter

Le seguenti condizioni possono applicarsi alla tua implementazione di System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter*:

Attacco dei parametri dinamici ai cmdlet Add-Content e Set-Content

I Add-Content cmdlet e Set-Content potrebbero richiedere parametri dinamici aggiuntivi che vengono aggiunti a runtime. Per fornire questi parametri dinamici, il fornitore di contenuti PowerShell di Windows deve implementare il metodo System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriterDynamicParameters* per gestire questi parametri. Questo metodo recupera parametri dinamici per l'elemento sul percorso indicato e restituisce un oggetto che possiede proprietà e campi con attributi di analisi simili a una classe cmdlet o a un oggetto System.Management.Automation.RuntimeDefinedParameterDictionary . L'runtime di Windows PowerShell utilizza l'oggetto restituito per aggiungere i parametri ai cmdlet.

Questo provider di container Windows PowerShell non implementa questo metodo. Tuttavia, il seguente codice è l'implementazione predefinita di questo metodo.

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

Cancellazione dei contenuti

Il tuo fornitore di contenuti implementa il metodo System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* a supporto del Clear-Content cmdlet. Questo metodo rimuove il contenuto dell'oggetto sul percorso specificato, ma lascia l'oggetto intatto.

Ecco l'implementazione del metodo System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* per questo fornitore.

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

Cose da ricordare sull'implementazione di contenuti trasparenti

Le seguenti condizioni possono applicarsi a un'implementazione di System.Management.Automation.Provider.IContentCmdletProvider.ClearContent*:

Attacco dei parametri dinamici al Clear-Content cmdlet

Il Clear-Content cmdlet potrebbe richiedere parametri dinamici aggiuntivi che vengono aggiunti a runtime. Per fornire questi parametri dinamici, il fornitore di contenuti PowerShell di Windows deve implementare il metodo System.Management.Automation.Provider.IContentCmdletProvider.ClearContentDynamicParameters* per gestire questi parametri. Questo metodo recupera i parametri dell'elemento sul percorso indicato. Questo metodo recupera parametri dinamici per l'elemento sul percorso indicato e restituisce un oggetto che possiede proprietà e campi con attributi di analisi simili a una classe cmdlet o a un oggetto System.Management.Automation.RuntimeDefinedParameterDictionary . L'runtime di Windows PowerShell utilizza l'oggetto restituito per aggiungere i parametri al cmdlet.

Questo provider di container Windows PowerShell non implementa questo metodo. Tuttavia, il seguente codice è l'implementazione predefinita di questo metodo.

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

Codice di esempio

Per il codice di esempio completo, vedi AccessDbProviderSample06 Code Sample.

Definizione dei tipi di oggetti e formattazione

Quando si scrive un provider, può essere necessario aggiungere membri agli oggetti esistenti o definire nuovi oggetti. Quando questo è fatto, devi creare un file Types che Windows PowerShell possa utilizzare per identificare i membri dell'oggetto e un file Format che definisce come l'oggetto viene visualizzato. Per ulteriori informazioni, vedi Estendere i tipi di oggetto e la formattazione.

Costruire il provider PowerShell di Windows

Vedi come registrare cmdlet, fornitori e applicazioni host.

Test del provider PowerShell di Windows

Quando il tuo provider PowerShell di Windows è stato registrato con Windows PowerShell, puoi testarlo eseguendo i cmdlet supportati nella riga di comando. Ad esempio, testa il fornitore di contenuti di esempio.

Usa il Get-Content cmdlet per recuperare il contenuto di un elemento specificato nella tabella del database sul percorso specificato dal Path parametro. Il parametro ReadCount specifica il numero di elementi che il lettore di contenuti definito deve leggere (predefinito 1). Con la seguente voce del comando, il cmdlet recupera due righe (elementi) dalla tabella e ne mostra il contenuto. Si noti che il seguente esempio di output utilizza un database Access fittizio.

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

Vedere anche

Creazione di provider PowerShell per Windows

Progetta il tuo provider PowerShell per Windows

Estensione dei tipi di oggetti e della formattazione

Implementa un provider PowerShell per la navigazione di Windows

Come registrare cmdlet, fornitori e applicazioni host

Windows PowerShell SDK

Guida per programmatori PowerShell di Windows