创建 Windows PowerShell 内容提供程序

本主题介绍如何创建 Windows PowerShell 提供程序,使用户能够作数据存储中项的内容。 因此,可以作项内容的提供程序称为 Windows PowerShell 内容提供程序。

注释

可以使用适用于 Windows Vista 和 .NET Framework 3.0 运行时组件的 Microsoft Windows 软件开发工具包下载此提供程序的 C# 源文件(AccessDBSampleProvider06.cs)。 有关下载说明,请参阅 如何安装 Windows PowerShell 并下载 Windows PowerShell SDK <PowerShell 示例> 目录中提供了下载的源文件。 有关其他 Windows PowerShell 提供程序实现的详细信息,请参阅 设计 Windows PowerShell 提供程序

定义 Windows PowerShell 内容提供程序类

Windows PowerShell 内容提供程序必须创建支持 System.Management.Automation.Provider.IContentCmdletProvider 接口的 .NET 类。 下面是本部分所述的项提供程序的类定义。

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

请注意,在此类定义中,System.Management.Automation.Provider.CmdletProviderAttribute 属性包含两个参数。 第一个参数指定 Windows PowerShell 使用的提供程序的用户友好名称。 第二个参数指定提供程序在命令处理期间向 Windows PowerShell 运行时公开的 Windows PowerShell 特定功能。 对于此提供程序,没有添加 Windows PowerShell 特定功能。

定义基类的功能

设计 Windows PowerShell 提供程序中所述,System.Management.Automation.Provider.NavigationCmdletProvider 类派生自提供不同提供程序功能的几个其他类。 因此,Windows PowerShell 内容提供程序通常定义这些类提供的所有功能。

有关如何实现添加特定于会话的初始化信息和释放提供程序使用的资源的功能的详细信息,请参阅 创建基本 Windows PowerShell 提供程序。 但是,大多数提供程序(包括此处所述的提供程序)都可以使用 Windows PowerShell 提供的此功能的默认实现。

若要访问数据存储,提供程序必须实现 System.Management.Automation.Provider.DriveCmdletProvider 基类的方法。 有关实现这些方法的详细信息,请参阅 创建 Windows PowerShell 驱动器提供程序

若要作数据存储的项(如获取、设置和清除项),提供程序必须实现由 System.Management.Automation.Provider.ItemCmdletProvider 基类提供的方法。 有关实现这些方法的详细信息,请参阅 创建 Windows PowerShell 项提供程序

若要处理多层数据存储,提供程序必须实现 System.Management.Automation.Provider.ContainerCmdletProvider 基类提供的方法。 有关实现这些方法的详细信息,请参阅 创建 Windows PowerShell 容器提供程序

若要支持递归命令、嵌套容器和相对路径,提供程序必须实现 System.Management.Automation.Provider.NavigationCmdletProvider 基类。 此外,此 Windows PowerShell 内容提供程序可以将 System.Management.Automation.Provider.IContentCmdletProvider 接口附加到 System.Management.Automation.Provider.NavigationCmdletProvider 基类,因此必须实现该类提供的方法。 有关详细信息,请参阅实现这些方法,请参阅 实现导航 Windows PowerShell 提供程序

实现内容读取器

若要从项读取内容,提供程序必须实现派生自 system.Management.Automation.Provider.IContentReader 的内容读取器类。 此提供程序的内容读取器允许访问数据表中某一行的内容。 内容读取器类定义一个 Read 方法,该方法从指示的行中检索数据,并返回表示该数据的列表、移动内容读取器的 Seek 方法、关闭内容读取器的 Close 方法,以及 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

实现内容编写器

若要将内容写入项,提供程序必须实现内容编写器类派生自 System.Management.Automation.Provider.IContentWriter。 内容编写器类定义一个 Write 方法,该方法写入指定的行内容、移动内容编写器的 Seek 方法、关闭内容编写器的 Close 方法,以及 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

检索内容读取器

若要从项获取内容,提供程序必须实现 System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* 以支持 Get-Content cmdlet。 此方法返回位于指定路径的项的内容读取器。 然后,可以打开读取器对象来读取内容。

下面是此提供程序此方法的 System.Management.Automation.Provider.IContentCmdletProvider.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
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

有关实现 GetContentReader 的注意事项

以下条件适用于 System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader*的实现:

将动态参数附加到 Get-Content Cmdlet

Get-Content cmdlet 可能需要在运行时动态指定的其他参数。 若要提供这些动态参数,Windows PowerShell 内容提供程序必须实现 System.Management.Automation.Provider.IContentCmdletProvider.GetContentReaderdynamicparameters* 方法。 此方法检索指示路径处的项的动态参数,并返回具有属性和字段的对象,其属性和字段与 cmdlet 类或 System.Management.Automation.RuntimeDefinedParameterDictionary 对象类似。 Windows PowerShell 运行时使用返回的对象将参数添加到 cmdlet。

此 Windows PowerShell 容器提供程序不实现此方法。 但是,以下代码是此方法的默认实现。

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

检索内容编写器

若要将内容写入项,提供程序必须实现 System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* 以支持 Set-ContentAdd-Content cmdlet。 此方法返回位于指定路径的项的内容编写器。

下面是此方法的 System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* 的实现。

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

有关实现 GetContentWriter 的注意事项

以下条件适用于 System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter*的实现:

将动态参数附加到 Add-Content 和 Set-Content Cmdlet

Add-ContentSet-Content cmdlet 可能需要添加运行时的其他动态参数。 若要提供这些动态参数,Windows PowerShell 内容提供程序必须实现 System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriterDynamicParameters* 方法来处理这些参数。 此方法检索指示路径处的项的动态参数,并返回具有属性和字段的对象,其属性和字段与 cmdlet 类或 System.Management.Automation.RuntimeDefinedParameterDictionary 对象类似。 Windows PowerShell 运行时使用返回的对象将参数添加到 cmdlet。

此 Windows PowerShell 容器提供程序不实现此方法。 但是,以下代码是此方法的默认实现。

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

清除内容

内容提供程序实现 System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* 方法,以支持 Clear-Content cmdlet。 此方法删除指定路径处的项的内容,但使项保持不变。

下面是此提供程序的 System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* 方法的实现。

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

有关实现 ClearContent 的注意事项

以下条件适用于 System.Management.Automation.Provider.IContentCmdletProvider.ClearContent*的实现:

将动态参数附加到 Clear-Content Cmdlet

Clear-Content cmdlet 可能需要在运行时添加的其他动态参数。 若要提供这些动态参数,Windows PowerShell 内容提供程序必须实现 System.Management.Automation.Provider.IContentCmdletProvider.ClearContentDynamicParameters* 方法来处理这些参数。 此方法检索指示路径处项的参数。 此方法检索指示路径处的项的动态参数,并返回具有属性和字段的对象,其属性和字段与 cmdlet 类或 System.Management.Automation.RuntimeDefinedParameterDictionary 对象类似。 Windows PowerShell 运行时使用返回的对象将参数添加到 cmdlet。

此 Windows PowerShell 容器提供程序不实现此方法。 但是,以下代码是此方法的默认实现。

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

代码示例

有关完整的示例代码,请参阅 AccessDbProviderSample06 代码示例

定义对象类型和格式

编写提供程序时,可能需要将成员添加到现有对象或定义新对象。 完成此作后,必须创建一个 Types 文件,Windows PowerShell 可用于标识对象的成员以及定义对象显示方式的格式化文件。 有关详细信息,请参阅 扩展对象类型和格式

生成 Windows PowerShell 提供程序

请参阅 如何注册 Cmdlet、提供程序和主机应用程序

测试 Windows PowerShell 提供程序

将 Windows PowerShell 提供程序注册到 Windows PowerShell 后,可以通过在命令行上运行支持的 cmdlet 来测试它。 例如,测试示例内容提供程序。

使用 Get-Content cmdlet 检索 Path 参数所指定路径的数据库表中指定项的内容。 ReadCount 参数指定要读取的已定义内容读取器的项目数(默认值 1)。 使用以下命令条目,cmdlet 从表中检索两行(项),并显示其内容。 请注意,以下示例输出使用虚构的 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

另请参阅

创建 Windows PowerShell 提供程序

设计 Windows PowerShell 提供程序

扩展对象类型和格式设置

实现导航 Windows PowerShell 提供程序

如何注册 Cmdlet、提供程序和主机应用程序

Windows PowerShell SDK

Windows PowerShell 程序员指南