Compartir a través de


Creación de un proveedor de contenido PowerShell para Windows

Este tema describe cómo crear un proveedor de PowerShell para Windows que permita al usuario manipular el contenido de los elementos en un almacén de datos. Como consecuencia, un proveedor que puede manipular el contenido de los elementos se denomina proveedor de contenido Windows PowerShell.

Nota:

Puedes descargar el archivo fuente (AccessDBSampleProvider06.cs) en C# de este proveedor utilizando el Microsoft Windows Software Development Kit para Windows Vista y los componentes de ejecución de .NET Framework 3.0. Para las instrucciones de descarga, consulta Cómo instalar Windows PowerShell y descargar el SDK de Windows PowerShell. Los archivos fuente descargados están disponibles en el <directorio de Ejemplos> de PowerShell. Para más información sobre otras implementaciones de proveedores de Windows PowerShell, consulte Diseñando su proveedor de PowerShell para Windows.

Defina la clase de proveedor de contenido PowerShell de Windows

Un proveedor de contenido PowerShell para Windows debe crear una clase .NET que soporte la interfaz System.Management.Automation.Provider.IContentCmdletProvider . Aquí está la definición de clase para el proveedor de ítems descrita en esta sección.

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

Obsábanse 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 fácil de usar para el proveedor que utiliza Windows PowerShell. El segundo parámetro especifica las capacidades específicas de PowerShell de Windows que el proveedor expone al entorno de ejecución de Windows PowerShell durante el procesamiento de comandos. Para este proveedor, no hay capacidades específicas de Windows para PowerShell añadidas.

Definir funcionalidad de la clase base

Como se describe en Design Your Windows PowerShell Provider, la clase System.Management.Automation.Provider.NavigationCmdletProvider deriva de varias otras clases que ofrecían diferentes funcionalidades de proveedores. Por tanto, un proveedor de contenido de Windows PowerShell suele definir toda la funcionalidad proporcionada por esas clases.

Para más información sobre cómo implementar funcionalidades para añadir información de inicialización específica de la sesión y para liberar recursos que utiliza el proveedor, véase Creación de un proveedor básico de PowerShell para Windows. Sin embargo, la mayoría de los proveedores, incluido el que se describe aquí, pueden usar la implementación predeterminada de esta funcionalidad que ofrece 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 más información sobre cómo implementar estos métodos, consulte Creación de un proveedor de unidades PowerShell para Windows.

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

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

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

Implementación de un lector de contenidos

Para leer contenido de un elemento, un proveedor debe implementar una clase lectora de contenido que derive de System.Management.Automation.Provider.IContentReader. El lector de contenido de este proveedor permite acceder al contenido de una fila en una tabla de datos. La clase 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 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 un redactor de contenidos

Para escribir contenido en un elemento, un proveedor debe implementar una clase de redactor de contenido derivada de System.Management.Automation.Provider.IContentWriter. La clase redactor de contenido define un método Write que escribe el contenido de la fila especificado, un método Seek que mueve al redactor de contenido, un método Close que cierra al redactor de contenido y un 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

Recuperación del lector de contenido

Para obtener contenido de un elemento, el proveedor debe implementar el System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* para soportar el Get-Content cmdlet. Este método devuelve el lector de contenido del elemento ubicado en la ruta especificada. El objeto lector puede abrirse para leer el contenido.

Aquí está la implementación de System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* para este método y 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 a recordar sobre la implementación de GetContentReader

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

Adjuntar parámetros dinámicos al Get-Content cmdlet

El Get-Content cmdlet 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 indicada y devuelve un objeto que tiene propiedades y campos con atributos de análisis similar a una clase cmdlet o un objeto System.Management.Automation.RuntimeDefinedParameterDictionary . El entorno de ejecución de Windows PowerShell utiliza el objeto devuelto para añadir los parámetros al cmdlet.

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

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

Recuperando al redactor de contenidos

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

Aquí está 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 a recordar sobre la implementación de GetContentWriter

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

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

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

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

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

Eliminar contenido

Tu proveedor de contenido implementa el método System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* en soporte del Clear-Content cmdlet. Este método elimina el contenido del elemento en la ruta especificada, pero deja el elemento intacto.

Aquí está 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 a recordar sobre la implementación de ClearContent

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

Adjuntar parámetros dinámicos al Clear-Content cmdlet

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

Este proveedor de contenedores de Windows PowerShell no implementa este método. Sin embargo, el siguiente código 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 ver el código de muestra completo, consulte AccessDbProviderSample06 Code Sample.

Definición de tipos de objetos y formato

Al escribir un proveedor, puede ser necesario añadir miembros a objetos existentes o definir nuevos objetos. Cuando esto se hace, debes 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 más información, véase Extensión de tipos de objetos y formato.

Construyendo el proveedor de PowerShell de Windows

Consulta cómo registrar cmdlets, proveedores y aplicaciones anfitrionas.

Probando el proveedor PowerShell de Windows

Cuando tu proveedor de PowerShell de Windows esté registrado en Windows PowerShell, puedes probarlo ejecutando los cmdlets compatibles en la línea de comandos. Por ejemplo, prueba el proveedor de contenido de muestra.

Utiliza el Get-Content cmdlet para recuperar el contenido del elemento especificado en la tabla de la base de datos en la ruta especificada por el Path parámetro. El ReadCount parámetro especifica el número de elementos que el lector de contenido definido debe leer (por defecto 1). Con la siguiente entrada de comando, el cmdlet recupera dos filas (elementos) de la tabla y muestra su contenido. Tenga en cuenta que el siguiente ejemplo de salida utiliza una base de datos Access ficticia.

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 PowerShell para Windows

Diseña tu proveedor PowerShell para Windows

Ampliación de los tipos de objetos y formato

Implementar un proveedor de PowerShell para Windows con navegación

Cómo registrar comandantes, proveedores y aplicaciones anfitrionas

Windows PowerShell SDK

Guía del programador de PowerShell para Windows