Compartir a través de


Creación de un proveedor de contenido de Windows PowerShell

En este tema se describe cómo crear un proveedor de Windows PowerShell que permita al usuario manipular el contenido de los elementos de un almacén de datos. Como consecuencia, un proveedor que puede manipular el contenido de los elementos se conoce como proveedor de contenido de Windows PowerShell.

Nota:

Puede descargar el archivo de código fuente de C# (AccessDBSampleProvider06.cs) para este proveedor mediante el Kit de desarrollo de software de Microsoft Windows para Windows Vista y componentes en tiempo de ejecución de .NET Framework 3.0. Para obtener instrucciones de descarga, consulte Cómo instalar Windows PowerShell y Descargar el SDK de Windows PowerShell. Los archivos de código fuente descargados están disponibles en el directorio>ejemplos de PowerShell de<. Para obtener más información sobre otras implementaciones del proveedor de Windows PowerShell, consulte Diseño del proveedor de Windows PowerShell.

Definir la clase de proveedor de contenido de Windows PowerShell

Un proveedor de contenido de Windows PowerShell debe crear una clase .NET que admita la interfaz System.Management.Automation.Provider.IContentCmdletProvider. Esta es la definición de clase para el proveedor de elementos descrito en esta sección.

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

Tenga en cuenta que, en esta definición de clase, el atributo System.Management.Automation.Provider.CmdletProviderAttribute incluye dos parámetros. El primer parámetro especifica un nombre descriptivo para el proveedor que usa Windows PowerShell. El segundo parámetro especifica las funcionalidades específicas de Windows PowerShell que el proveedor expone al entorno de ejecución de Windows PowerShell durante el procesamiento de comandos. Para este proveedor, no se han agregado funcionalidades específicas de Windows PowerShell.

Definir la funcionalidad de la clase base

Tal y como se describe en Diseñar el proveedor de Windows PowerShell, la clase System.Management.Automation.Provider.NavigationCmdletProvider deriva de otras clases que proporcionan distintas funcionalidades del proveedor. Un proveedor de contenido de Windows PowerShell, por lo tanto, normalmente define toda la funcionalidad proporcionada por esas clases.

Para obtener más información sobre cómo implementar la funcionalidad para agregar información de inicialización específica de la sesión y para liberar recursos que usa el proveedor, vea Creación de un proveedor básico de Windows PowerShell. Sin embargo, la mayoría de los proveedores, incluido el proveedor descrito aquí, pueden usar la implementación predeterminada de esta funcionalidad proporcionada por Windows PowerShell.

Para acceder al almacén de datos, el proveedor debe implementar los métodos de la clase base system.Management.Automation.Provider.DriveCmdletProvider . Para obtener más información sobre cómo implementar estos métodos, vea Creación de un proveedor de unidades de Windows PowerShell.

Para manipular los elementos de un almacén de datos, como obtener, establecer y borrar elementos, el proveedor debe implementar los métodos proporcionados por la clase base System.Management.Automation.Provider.ItemCmdletProvider. Para obtener más información sobre cómo implementar estos métodos, vea Creación de un proveedor de elementos de Windows PowerShell.

Para trabajar en almacenes de datos de varias capas, el proveedor debe implementar los métodos proporcionados por la clase base System.Management.Automation.Provider.ContainerCmdletProvider. Para obtener más información sobre cómo implementar estos métodos, vea Creación de un proveedor de contenedores de Windows PowerShell.

Para admitir comandos recursivos, contenedores anidados y rutas de acceso relativas, el proveedor debe implementar la clase base System.Management.Automation.Provider.NavigationCmdletProvider. Además, este proveedor de contenido de Windows PowerShell puede asociar System.Management.Automation.Provider.IContentCmdletProvider interfaz a la interfaz System.Management.Automation.Provider.NavigationCmdletProvider clase base y, por tanto, debe implementar los métodos proporcionados por esa clase. Para obtener más información, consulte implementación de esos métodos, consulte Implementar un proveedor de Windows PowerShell de navegación.

Implementación de un lector de contenido

Para leer contenido de un elemento, un proveedor debe implementar una clase de lector de contenido que derive de System.Management.Automation.Provider.IContentReader. El lector de contenido de este proveedor permite el acceso al contenido de una fila de una tabla de datos. La clase de lector de contenido define un método Read que recupera los datos de la fila indicada y devuelve una lista que representa esos datos, un método Seek que mueve el lector de contenido, un método Close que cierra el lector de contenido y un método Dispose.

public class AccessDBContentReader : IContentReader
{
    // A provider instance is required so as to get "content"
    private AccessDBProvider provider;
    private string path;
    private long currentOffset;

    internal AccessDBContentReader(string path, AccessDBProvider provider)
    {
        this.path = path;
        this.provider = provider;
    }

    /// <summary>
    /// Read the specified number of rows from the source.
    /// </summary>
    /// <param name="readCount">The number of items to 
    /// return.</param>
    /// <returns>An array of elements read.</returns>
    public IList Read(long readCount)
    {
        // Read the number of rows specified by readCount and increment
        // offset
        string tableName;
        int rowNumber;
        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        Collection<DatabaseRowInfo> rows =
            provider.GetRows(tableName);
        Collection<DataRow> results = new Collection<DataRow>();

        if (currentOffset < 0 || currentOffset >= rows.Count)
        {
            return null;
        }

        int rowsRead = 0;

        while (rowsRead < readCount && currentOffset < rows.Count)
        {
            results.Add(rows[(int)currentOffset].Data);
            rowsRead++;
            currentOffset++;
        }

        return results;
    } // Read

    /// <summary>
    /// Moves the content reader specified number of rows from the 
    /// origin
    /// </summary>
    /// <param name="offset">Number of rows to offset</param>
    /// <param name="origin">Starting row from which to offset</param>
    public void Seek(long offset, System.IO.SeekOrigin origin)
    {
        // get the number of rows in the table which will help in
        // calculating current position
        string tableName;
        int rowNumber;

        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        if (type == PathType.Invalid)
        {
            throw new ArgumentException("Path specified must represent a table or a row :" + path);
        }

        if (type == PathType.Table)
        {
            Collection<DatabaseRowInfo> rows = provider.GetRows(tableName);

            int numRows = rows.Count;

            if (offset > rows.Count)
            {
                throw new
                       ArgumentException(
                           "Offset cannot be greater than the number of rows available"
                                        );
            }

            if (origin == System.IO.SeekOrigin.Begin)
            {
                // starting from Beginning with an index 0, the current offset
                // has to be advanced to offset - 1
                currentOffset = offset - 1;
            }
            else if (origin == System.IO.SeekOrigin.End)
            {
                // starting from the end which is numRows - 1, the current
                // offset is so much less than numRows - 1
                currentOffset = numRows - 1 - offset;
            }
            else
            {
                // calculate from the previous value of current offset
                // advancing forward always
                currentOffset += offset;
            }
        } // if (type...
        else
        {
            // for row, the offset will always be set to 0
            currentOffset = 0;
        }

    } // Seek

    /// <summary>
    /// Closes the content reader, so all members are reset
    /// </summary>
    public void Close()
    {
        Dispose();
    } // Close

    /// <summary>
    /// Dispose any resources being used
    /// </summary>
    public void Dispose()
    {
        Seek(0, System.IO.SeekOrigin.Begin);
        
        GC.SuppressFinalize(this);
    } // Dispose
} // AccessDBContentReader

Implementación de un escritor de contenido

Para escribir contenido en un elemento, un proveedor debe implementar una clase de escritor de contenido derivada de System.Management.Automation.Provider.IContentWriter. La clase content writer define un método Write que escribe el contenido de fila especificado, un método Seek que mueve el escritor de contenido, un método Close que cierra el escritor de contenido y un método Dispose.

public class AccessDBContentWriter : IContentWriter
{
    // A provider instance is required so as to get "content"
    private AccessDBProvider provider;
    private string path;
    private long currentOffset;

    internal AccessDBContentWriter(string path, AccessDBProvider provider)
    {
        this.path = path;
        this.provider = provider;
    }

    /// <summary>
    /// Write the specified row contents in the source
    /// </summary>
    /// <param name="content"> The contents to be written to the source.
    /// </param>
    /// <returns>An array of elements which were successfully written to 
    /// the source</returns>
    /// 
    public IList Write(IList content)
    {
        if (content == null)
        {
            return null;
        }

        // Get the total number of rows currently available it will 
        // determine how much to overwrite and how much to append at
        // the end
        string tableName;
        int rowNumber;
        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        if (type == PathType.Table)
        {
            OdbcDataAdapter da = provider.GetAdapterForTable(tableName);
            if (da == null)
            {
                return null;
            }

            DataSet ds = provider.GetDataSetForTable(da, tableName);
            DataTable table = provider.GetDataTable(ds, tableName);

            string[] colValues = (content[0] as string).Split(',');

            // set the specified row
            DataRow row = table.NewRow();

            for (int i = 0; i < colValues.Length; i++)
            {
                if (!String.IsNullOrEmpty(colValues[i]))
                {
                    row[i] = colValues[i];
                }
            }

            //table.Rows.InsertAt(row, rowNumber);
            // Update the table
            table.Rows.Add(row);
            da.Update(ds, tableName);
            
        }
        else 
        {
            throw new InvalidOperationException("Operation not supported. Content can be added only for tables");
        }

        return null;
    } // Write

    /// <summary>
    /// Moves the content reader specified number of rows from the 
    /// origin
    /// </summary>
    /// <param name="offset">Number of rows to offset</param>
    /// <param name="origin">Starting row from which to offset</param>
    public void Seek(long offset, System.IO.SeekOrigin origin)
    {
        // get the number of rows in the table which will help in
        // calculating current position
        string tableName;
        int rowNumber;

        PathType type = provider.GetNamesFromPath(path, out tableName, out rowNumber);

        if (type == PathType.Invalid)
        {
            throw new ArgumentException("Path specified should represent either a table or a row : " + path);
        }

        Collection<DatabaseRowInfo> rows =
               provider.GetRows(tableName);

        int numRows = rows.Count;

        if (offset > rows.Count)
        {
            throw new
                   ArgumentException(
                       "Offset cannot be greater than the number of rows available"
                                           );
        }

        if (origin == System.IO.SeekOrigin.Begin)
        {
            // starting from Beginning with an index 0, the current offset
            // has to be advanced to offset - 1
            currentOffset = offset - 1;
        }
        else if (origin == System.IO.SeekOrigin.End)
        {
            // starting from the end which is numRows - 1, the current
            // offset is so much less than numRows - 1
            currentOffset = numRows - 1 - offset;
        }
        else
        {
            // calculate from the previous value of current offset
            // advancing forward always
            currentOffset += offset;
        }

    } // Seek

    /// <summary>
    /// Closes the content reader, so all members are reset
    /// </summary>
    public void Close()
    {
        Dispose();
    } // Close

    /// <summary>
    /// Dispose any resources being used
    /// </summary>
    public void Dispose()
    {
        Seek(0, System.IO.SeekOrigin.Begin);

        GC.SuppressFinalize(this);
    } // Dispose
} // AccessDBContentWriter

Recuperación del lector de contenido

Para obtener contenido de un elemento, el proveedor debe implementar el System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* para admitir el cmdlet Get-Content. Este método devuelve el lector de contenido del elemento ubicado en la ruta de acceso especificada. A continuación, se puede abrir el objeto lector para leer el contenido.

Esta es la implementación de System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* para este método para este proveedor.

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

Cosas que recordar sobre la implementación de GetContentReader

Las condiciones siguientes pueden aplicarse a una implementación de System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader*:

Asociación de parámetros dinámicos al cmdlet de Get-Content

El cmdlet Get-Content puede requerir parámetros adicionales que se especifican dinámicamente en tiempo de ejecución. Para proporcionar estos parámetros dinámicos, el proveedor de contenido de Windows PowerShell debe implementar el método System.Management.Automation.Provider.IContentCmdletProvider.GetContentReaderdynamicparameters*. Este método recupera parámetros dinámicos para el elemento en la ruta de acceso indicada y devuelve un objeto que tiene propiedades y campos con atributos de análisis similares a una clase de cmdlet o un objeto System.Management.Automation.RuntimeDefinedParameterDictionary. El entorno de ejecución de Windows PowerShell usa el objeto devuelto para agregar los parámetros al cmdlet .

Este proveedor de contenedores de Windows PowerShell no implementa este método. Sin embargo, el código siguiente es la implementación predeterminada de este método.

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

Recuperación del escritor de contenido

Para escribir contenido en un elemento, el proveedor debe implementar el System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* para admitir los cmdlets Set-Content y Add-Content. Este método devuelve el escritor de contenido para el elemento ubicado en la ruta de acceso especificada.

Esta es la implementación de System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* para este 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);
}

Cosas que recordar sobre la implementación de GetContentWriter

Las condiciones siguientes pueden aplicarse a la implementación de System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter*:

Adjuntar parámetros dinámicos a los cmdlets de Add-Content y Set-Content

Los cmdlets Add-Content y Set-Content pueden requerir parámetros dinámicos adicionales que se agregan a un entorno de ejecución. Para proporcionar estos parámetros dinámicos, el proveedor de contenido de Windows PowerShell debe implementar el método System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriterDynamicParameters* para controlar estos parámetros. Este método recupera parámetros dinámicos para el elemento en la ruta de acceso indicada y devuelve un objeto que tiene propiedades y campos con atributos de análisis similares a una clase de cmdlet o un objeto System.Management.Automation.RuntimeDefinedParameterDictionary. El entorno de ejecución de Windows PowerShell usa el objeto devuelto para agregar los parámetros a los cmdlets.

Este proveedor de contenedores de Windows PowerShell no implementa este método. Sin embargo, el código siguiente es la implementación predeterminada de este método.

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

Borrar contenido

El proveedor de contenido implementa el método System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* compatible con el cmdlet Clear-Content. Este método quita el contenido del elemento en la ruta de acceso especificada, pero deja intacto el elemento.

Esta es la implementación del método System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* para este proveedor.

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

Cosas que recordar sobre la implementación de ClearContent

Las condiciones siguientes pueden aplicarse a una implementación de System.Management.Automation.Provider.IContentCmdletProvider.ClearContent*:

Asociación de parámetros dinámicos al cmdlet de Clear-Content

El cmdlet Clear-Content podría requerir parámetros dinámicos adicionales que se agregan en tiempo de ejecución. Para proporcionar estos parámetros dinámicos, el proveedor de contenido de Windows PowerShell debe implementar el método System.Management.Automation.Provider.IContentCmdletProvider.ClearContentDynamicParameters* para controlar estos parámetros. Este método recupera los parámetros del elemento en la ruta de acceso indicada. Este método recupera parámetros dinámicos para el elemento en la ruta de acceso indicada y devuelve un objeto que tiene propiedades y campos con atributos de análisis similares a una clase de cmdlet o un objeto System.Management.Automation.RuntimeDefinedParameterDictionary. El entorno de ejecución de Windows PowerShell usa el objeto devuelto para agregar los parámetros al cmdlet .

Este proveedor de contenedores de Windows PowerShell no implementa este método. Sin embargo, el código siguiente es la implementación predeterminada de este método.

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

Ejemplo de código

Para obtener código de ejemplo completo, consulte ejemplo de código AccessDbProviderSample06.

Definición de tipos de objeto y formato

Al escribir un proveedor, puede ser necesario agregar miembros a objetos existentes o definir nuevos objetos. Una vez hecho esto, debe crear un archivo types que Windows PowerShell pueda usar para identificar los miembros del objeto y un archivo Format que defina cómo se muestra el objeto. Para obtener más información, vea extensión de tipos de objeto y formato.

Compilación del proveedor de Windows PowerShell

Consulte Registro de cmdlets, proveedores y aplicaciones host.

Probar el proveedor de Windows PowerShell

Cuando el proveedor de Windows PowerShell se haya registrado con Windows PowerShell, puede probarlo ejecutando los cmdlets admitidos en la línea de comandos. Por ejemplo, pruebe el proveedor de contenido de ejemplo.

Use el cmdlet Get-Content para recuperar el contenido del elemento especificado en la tabla de base de datos en la ruta de acceso especificada por el parámetro Path. El parámetro ReadCount especifica el número de elementos para que el lector de contenido definido lea (valor predeterminado 1). Con la siguiente entrada de comando, el cmdlet recupera dos filas (elementos) de la tabla y muestra su contenido. Tenga en cuenta que la siguiente salida de ejemplo usa una base de datos ficticia de 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

Véase también

creación de proveedores de Windows PowerShell

diseñar el proveedor de Windows PowerShell

extensión de tipos de objeto y formato

Implementar un proveedor de Windows PowerShell de navegación

Registro de cmdlets, proveedores y aplicaciones host

del SDK de Windows PowerShell

guía del programador de Windows PowerShell