Compartilhar via


Criando um Provedor de Conteúdo PowerShell para Windows

Este tópico descreve como criar um provedor de PowerShell para Windows que permita ao usuário manipular o conteúdo dos itens em um armazenamento de dados. Como consequência, um provedor que pode manipular o conteúdo de itens é chamado de provedor de conteúdo Windows PowerShell.

Observação

Você pode baixar o arquivo fonte C# (AccessDBSampleProvider06.cs) deste provedor usando o Microsoft Windows Software Development Kit para Windows Vista e os Componentes de Tempo de Execução do .NET Framework 3.0. Para instruções de download, veja Como Instalar o Windows PowerShell e Baixar o Windows PowerShell SDK. Os arquivos fonte baixados estão disponíveis no <diretório PowerShell Samples> . Para mais informações sobre outras implementações de provedores Windows PowerShell, veja Designing Your Windows PowerShell Provider.

Defina a Classe de Provedor de Conteúdo PowerShell do Windows

Um provedor de conteúdo PowerShell para Windows deve criar uma classe .NET que suporte a interface System.Management.Automation.Provider.IContentCmdletProvider . Aqui está a definição de classe para o provedor de itens descrita nesta seção.

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

Note que, nesta definição de classe, o atributo System.Management.Automation.Provider.CmdletProviderAttribute inclui dois parâmetros. O primeiro parâmetro especifica um nome amigável para o provedor usado pelo Windows PowerShell. O segundo parâmetro especifica as capacidades específicas do Windows PowerShell que o provedor expõe ao tempo de execução do Windows PowerShell durante o processamento de comandos. Para este provedor, não há recursos adicionais específicos do PowerShell do Windows.

Defina a funcionalidade da classe base

Como descrito em Design Your Windows PowerShell Provider, a classe System.Management.Automation.Provider.NavigationCmdletProvider deriva de várias outras classes que forneciam funcionalidades diferentes do provedor. Portanto, um provedor de conteúdo do Windows PowerShell normalmente define toda a funcionalidade fornecida por essas classes.

Para mais informações sobre como implementar funcionalidades para adicionar informações específicas de inicialização de sessão e para liberar recursos usados pelo provedor, veja Criando um Provedor Básico de PowerShell para Windows. No entanto, a maioria dos provedores, incluindo o fornecido aqui, pode usar a implementação padrão dessa funcionalidade fornecida pelo Windows PowerShell.

Para acessar o armazenamento de dados, o provedor deve implementar os métodos da classe base System.Management.Automation.Provider.DriveCmdletProvider . Para mais informações sobre como implementar esses métodos, veja Criando um Provedor de Unidades PowerShell para Windows.

Para manipular os itens de um armazenamento de dados, como obtenção, configuração e compensação de itens, o provedor deve implementar os métodos fornecidos pela classe base System.Management.Automation.Provider.ItemCmdletProvider . Para mais informações sobre como implementar esses métodos, veja Criando um Provedor de Itens do Windows PowerShell.

Para trabalhar em repositórios de dados multicamadas, o provedor deve implementar os métodos fornecidos pela classe base System.Management.Automation.Provider.ContainerCmdletProvider . Para mais informações sobre como implementar esses métodos, veja Criando um Provedor de Contêineres para Windows PowerShell.

Para suportar comandos recursivos, contêineres aninhados e caminhos relativos, o provedor deve implementar a classe base System.Management.Automation.Provider.NavigationCmdletProvider . Além disso, esse provedor de conteúdo PowerShell do Windows pode anexar a interface System.Management.Automation.Provider.IContentCmdletProvider à classe base System.Management.Automation.Provider.NavigationCmdletProvider , e portanto deve implementar os métodos fornecidos por essa classe. Para mais informações, veja implementar esses métodos, veja Implementar um Provedor PowerShell para Navegação Windows.

Implementando um Leitor de Conteúdo

Para ler conteúdo de um item, um provedor deve implementar uma classe de leitor de conteúdo que deriva de System.Management.Automation.Provider.IContentReader. O leitor de conteúdo desse provedor permite o acesso ao conteúdo de uma linha em uma tabela de dados. A classe content reader define um método Read que recupera os dados da linha indicada e retorna uma lista representando esses dados, um método Seek que move o leitor de conteúdo, um método Close que fecha o leitor de conteúdo e um método 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

Implementando um Redator de Conteúdo

Para escrever conteúdo em um item, um provedor deve implementar uma classe de escritor de conteúdo derivada de System.Management.Automation.Provider.IContentWriter. A classe de escritor de conteúdo define um método Write que escreve o conteúdo da linha especificado, um método Seek que move o redator de conteúdo, um método Close que fecha o redator de conteúdo e um método 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

Recuperando o Leitor de Conteúdo

Para obter conteúdo de um item, o provedor deve implementar o System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* para suportar o Get-Content cmdlet. Esse método retorna o leitor de conteúdo do item localizado no caminho especificado. O objeto leitor pode então ser aberto para ler o conteúdo.

Aqui está a implementação de System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* para este método para este provedor.

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

Coisas para Lembrar Sobre a Implementação do GetContentReader

As seguintes condições podem se aplicar a uma implementação de System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader*:

Anexando Parâmetros Dinâmicos ao Cmdlet Get-Content

O Get-Content cmdlet pode exigir parâmetros adicionais que são especificados dinamicamente em tempo de execução. Para fornecer esses parâmetros dinâmicos, o provedor de conteúdo PowerShell do Windows deve implementar o método System.Management.Automation.Provider.IContentCmdletProvider.GetContentReaderdynamicparameters* . Esse método recupera parâmetros dinâmicos para o item no caminho indicado e retorna um objeto que possui propriedades e campos com atributos de análise semelhantes a uma classe cmdlet ou a um objeto System.Management.Automation.RuntimeDefinedParameterDictionary . O runtime do Windows PowerShell usa o objeto retornado para adicionar os parâmetros ao cmdlet.

Este provedor de contêineres Windows PowerShell não implementa esse método. No entanto, o código a seguir é a implementação padrão desse método.

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

Recuperando o Redator de Conteúdo

Para escrever conteúdo em um item, o provedor deve implementar o System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* para suportar os Set-Content cmdlets e Add-Content . Esse método retorna o redator de conteúdo para o item localizado no caminho especificado.

Aqui está a implementação do System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* para esse método.

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

Coisas para Lembrar Sobre a Implementação do GetContentWriter

As seguintes condições podem se aplicar à sua implementação de System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter*:

Anexando parâmetros dinâmicos aos cmdlets Add-Content e Set-Content

Os Add-Content cmdlets e Set-Content podem exigir parâmetros dinâmicos adicionais que são adicionados em tempo de execução. Para fornecer esses parâmetros dinâmicos, o provedor de conteúdo do Windows PowerShell deve implementar o método System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriterDynamicParameters* para lidar com esses parâmetros. Esse método recupera parâmetros dinâmicos para o item no caminho indicado e retorna um objeto que possui propriedades e campos com atributos de análise semelhantes a uma classe cmdlet ou a um objeto System.Management.Automation.RuntimeDefinedParameterDictionary . O runtime do Windows PowerShell usa o objeto retornado para adicionar os parâmetros aos cmdlets.

Este provedor de contêineres Windows PowerShell não implementa esse método. No entanto, o código a seguir é a implementação padrão desse método.

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

Limpando conteúdo

Seu provedor de conteúdo implementa o método System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* em suporte ao Clear-Content cmdlet. Esse método remove o conteúdo do item no caminho especificado, mas mantém o item intacto.

Aqui está a implementação do método System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* para esse provedor.

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

Coisas para Lembrar Sobre a Implementação do ClearContent

As seguintes condições podem se aplicar a uma implementação de System.Management.Automation.Provider.IContentCmdletProvider.ClearContent*:

Anexando Parâmetros Dinâmicos ao Cmdlet Clear-Content

O Clear-Content cmdlet pode exigir parâmetros dinâmicos adicionais que são adicionados em tempo de execução. Para fornecer esses parâmetros dinâmicos, o provedor de conteúdo do Windows PowerShell deve implementar o método System.Management.Automation.Provider.IContentCmdletProvider.ClearContentDynamicParameters* para lidar com esses parâmetros. Esse método recupera os parâmetros do item no caminho indicado. Esse método recupera parâmetros dinâmicos para o item no caminho indicado e retorna um objeto que possui propriedades e campos com atributos de análise semelhantes a uma classe cmdlet ou a um objeto System.Management.Automation.RuntimeDefinedParameterDictionary . O runtime do Windows PowerShell usa o objeto retornado para adicionar os parâmetros ao cmdlet.

Este provedor de contêineres Windows PowerShell não implementa esse método. No entanto, o código a seguir é a implementação padrão desse método.

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

Exemplo de código

Para um código de exemplo completo, veja AccessDbProviderSample06 Code Sample.

Definição de Tipos de Objetos e Formatação

Ao escrever um provedor, pode ser necessário adicionar membros a objetos existentes ou definir novos objetos. Quando isso for feito, você deve criar um arquivo Types que o Windows PowerShell possa usar para identificar os membros do objeto e um arquivo Format que defina como o objeto é exibido. Para mais informações, veja Estendendo Tipos de Objeto e Formatação.

Construindo o Provedor Windows PowerShell

Veja como registrar cmdlets, provedores e aplicações host.

Testando o Provedor Windows PowerShell

Quando seu provedor de Windows PowerShell estiver registrado no Windows PowerShell, você pode testá-lo executando os cmdlets suportados na linha de comando. Por exemplo, teste o provedor de conteúdo de exemplo.

Use o Get-Content cmdlet para recuperar o conteúdo do item especificado na tabela do banco de dados no caminho especificado pelo Path parâmetro. O ReadCount parâmetro especifica o número de itens para o leitor de conteúdo definido ler (padrão 1). Com a entrada do comando seguinte, o cmdlet recupera duas linhas (itens) da tabela e exibe seu conteúdo. Note que o seguinte exemplo de resultado usa um banco de dados fictício do 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

Consulte Também

Criando provedores PowerShell para Windows

Projete seu provedor PowerShell do Windows

Estendendo os Tipos de Objetos e Formatação

Implemente um provedor de PowerShell para Navegação do Windows

Como Registrar Comandantes, Provedores e Aplicações Host

Windows PowerShell SDK

Guia do Programador do Windows PowerShell