创建 Windows PowerShell 容器提供者

本主题描述如何创建一个能够在多层数据存储上运行的 Windows PowerShell 提供者。 对于这种类型的数据存储,存储的顶层包含根项,每个后续层级被称为子项的节点。 通过允许用户在这些子节点上工作,用户可以通过数据存储进行分层交互。

能够在多层数据存储上工作的提供者被称为Windows PowerShell容器提供者。 但请注意,Windows PowerShell 容器提供者只能在存在一个容器(无嵌套容器)且包含项目时使用。 如果存在嵌套容器,那么你必须实现一个 Windows PowerShell 导航提供者。 有关实现 Windows PowerShell 导航提供者的更多信息,请参见 创建 Windows PowerShell 导航提供者

注释

您可以使用 Windows Vista 和 .NET Framework 3.0 运行时组件的 Microsoft Windows 软件开发工具包下载该提供者的 C# 源文件(AccessDBSampleProvider04.cs)。 有关下载说明,请参见 《如何安装Windows PowerShell》和《Windows PowerShell SDK》。 下载的源文件可在 <PowerShell Samples> 目录中获取。 有关其他 Windows PowerShell 提供者实现的更多信息,请参见 “设计你的 Windows PowerShell 提供者”。

这里描述的 Windows PowerShell 容器提供者将数据库定义为其单一容器,数据库的表和行定义为容器中的项。

注意

请注意,这种设计假设数据库有一个带有名称 ID 的字段,且字段类型为 LongInteger。

定义 Windows PowerShell 容器提供者类

Windows PowerShell 容器提供者必须定义一个从 System.Management.Automation.Provider.ContainerCmdletProvider 基类派生的 .NET 类。 以下是本节中描述的 Windows PowerShell 容器提供者的类定义。

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

注意,在该类定义中, System.Management.Automation.Provider.CmdletProviderAttribute 属性包含两个参数。 第一个参数指定了 Windows PowerShell 所用提供者的用户友好名称。 第二个参数指定了 Windows PowerShell 在命令处理过程中向 Windows PowerShell 运行时暴露的具体功能。 对于该提供者,没有添加任何 Windows PowerShell 特定的功能。

定义基础功能

如《 设计你的 Windows PowerShell 提供者》中所述, System.Management.Automation.Provider.ContainerCmdletProvider 类源自其他提供不同提供者功能的类。 因此,Windows PowerShell 容器提供者需要定义这些类提供的所有功能。

要实现添加会话特定初始化信息和释放提供者使用的资源的功能,请参见 创建基础 Windows PowerShell 提供者。 然而,大多数提供者(包括此处描述的提供者)可以使用 Windows PowerShell 提供的该功能的默认实现。

为了访问数据存储,提供者必须实现 System.Management.Automation.Provider.DriveCmdletProvider 基类的方法。 关于实现这些方法的更多信息,请参见 创建 Windows PowerShell 驱动器提供者

为了作数据存储中的项目,如获取、设置和清除项目,提供者必须实现 System.Management.Automation.Provider.ItemCmdletProvider 基类提供的方法。 关于实现这些方法的更多信息,请参见 创建 Windows PowerShell 项提供者

取回子物品

要检索子项,Windows PowerShell 容器提供者必须覆盖 System.Management.Automation.Provider.ContainerCmdletProvider.GetChildItems* 方法,以支持该 Get-ChildItem 命令调用。 该方法从数据存储中检索子项,并将其写入流水线作为对象。 如果 recurse 指定了 cmdlet 的参数,方法会检索所有子节点,无论它们处于哪个层级。 如果未指定参数, recurse 方法只检索单个子节点层。

这是该提供者 System.Management.Automation.Provider.ContainerCmdletProvider.GetChildItems* 方法的实现。 注意,当路径指示为Access数据库时,该方法检索所有数据库表中的子项;如果路径指示为数据表,则从该表的行中检索子项。

protected override void GetChildItems(string path, bool recurse)
{
    // If path represented is a drive then the children in the path are 
    // tables. Hence all tables in the drive represented will have to be
    // returned
    if (PathIsDrive(path))
    {
        foreach (DatabaseTableInfo table in GetTables())
        {
            WriteItemObject(table, path, true);

            // if the specified item exists and recurse has been set then 
            // all child items within it have to be obtained as well
            if (ItemExists(path) && recurse)
            {
                GetChildItems(path + pathSeparator + table.Name, recurse);
            }
        } // foreach (DatabaseTableInfo...
    } // if (PathIsDrive...
    else
    {
        // Get the table name, row number and type of path from the
        // path specified
        string tableName;
        int rowNumber;

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

        if (type == PathType.Table)
        {
            // Obtain all the rows within the table
            foreach (DatabaseRowInfo row in GetRows(tableName))
            {
                WriteItemObject(row, path + pathSeparator + row.RowNumber,
                        false);
            } // foreach (DatabaseRowInfo...
        }
        else if (type == PathType.Row)
        {
            // In this case the user has directly specified a row, hence
            // just give that particular row
            DatabaseRowInfo row = GetRow(tableName, rowNumber);
            WriteItemObject(row, path + pathSeparator + row.RowNumber,
                        false);
        }
        else
        {
            // In this case, the path specified is not valid
            ThrowTerminatingInvalidPathException(path);
        }
    } // else
} // GetChildItems

关于实现GetChildItems需要记住的事项

以下条件可能适用于您的 System.Management.Automation.Provider.ContainerCmdletProvider.GetChildItems*的实现:

将动态参数附加到 Get-ChildItem cmdlet

有时调用System.Management.Automation.Provider.ContainerCmdletProvider.GetChildItems*Get-ChildItemcmdlet需要在运行时动态指定额外参数。 为了提供这些动态参数,Windows PowerShell 容器提供者必须实现 System.Management.Automation.Provider.ContainerCmdletProvider.GetChildItemsDynamicParameters* 方法。 该方法检索指定路径上的动态参数,返回一个具有类似 cmdlet 类或 System.Management.Automation.RuntimeDefinedParameterDictionary 对象的属性和字段的对象。 Windows PowerShell 运行时使用返回的对象将参数添加到 Get-ChildItem cmdlet。

该 Windows PowerShell 容器提供者未实现此方法。 不过,以下代码是该方法的默认实现。

检索子物品名称

为了获取子项的名称,Windows PowerShell 容器提供者必须覆盖 System.Management.Automation.Provider.ContainerCmdletProvider.GetChildNames* 方法,以支持在参数指定时调用 Get-ChildItem 该命令 Name 。 该方法会检索指定路径的子项名称,或者如果 returnAllContainers cmdlet 参数指定了,则获取所有容器的子项名称。 子名称是路径的叶子部分。 例如,路径 C:\windows\system32\abc.dll 的子名称是“abc.dll”。 目录 C:\windows\system32 的子名称是“system32”。

这是该提供者 System.Management.Automation.Provider.ContainerCmdletProvider.GetChildNames* 方法的实现。 注意,如果指定的路径指向 Access 数据库(驱动器),则检索表名;如果路径指示表,则检索行号。

protected override void GetChildNames(string path,
                              ReturnContainers returnContainers)
{
    // If the path represented is a drive, then the child items are
    // tables. get the names of all the tables in the drive.
    if (PathIsDrive(path))
    {
        foreach (DatabaseTableInfo table in GetTables())
        {
            WriteItemObject(table.Name, path, true);
        } // foreach (DatabaseTableInfo...
    } // if (PathIsDrive...
    else
    {
        // Get type, table name and row number from path specified
        string tableName;
        int rowNumber;

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

        if (type == PathType.Table)
        {
            // Get all the rows in the table and then write out the 
            // row numbers.
            foreach (DatabaseRowInfo row in GetRows(tableName))
            {
                WriteItemObject(row.RowNumber, path, false);
            } // foreach (DatabaseRowInfo...
        }
        else if (type == PathType.Row)
        {
            // In this case the user has directly specified a row, hence
            // just give that particular row
            DatabaseRowInfo row = GetRow(tableName, rowNumber);

            WriteItemObject(row.RowNumber, path, false);
        }
        else
        {
            ThrowTerminatingInvalidPathException(path);
        }
    } // else
} // GetChildNames

关于实现GetChildNames需要注意的事项

以下条件可能适用于您的 System.Management.Automation.Provider.ContainerCmdletProvider.GetChildItems*的实现:

将动态参数附加到 Get-ChildItem cmdlet(名称)

有时 Get-ChildItem cmdlet(带参数 Name )需要运行时动态指定额外参数。 为了提供这些动态参数,Windows PowerShell 容器提供者必须实现 System.Management.Automation.Provider.ContainerCmdletProvider.GetChildNamesDynamicParameters* 方法。 该方法检索指定路径上项目的动态参数,返回一个具有类似于 cmdlet 类或 System.Management.Automation.RuntimeDefinedParameterDictionary 对象的属性和字段的对象。 Windows PowerShell 运行时使用返回的对象将参数添加到 Get-ChildItem cmdlet。

该提供者未实现该方法。 不过,以下代码是该方法的默认实现。

更名项目

要重命名一个项目,Windows PowerShell 容器提供者必须覆盖 System.Management.Automation.Provider.ContainerCmdletProvider.RenameItem* 方法,以支持该 Rename-Item 命令的调用。 该方法将指定路径上的项目名称更改为新名称。 新名称必须始终相对于父项(容器)命名。

该提供者不会覆盖 System.Management.Automation.Provider.ContainerCmdletProvider.RenameItem* 方法。 不过,以下是默认实现。

实现RenameItem需要注意的事项

以下条件可能适用于您的 System.Management.Automation.Provider.ContainerCmdletProvider.RenameItem*的实现:

将动态参数附加到 Rename-Item cmdlet

有时 Rename-Item cmdlet 需要在运行时动态指定额外参数。 为了提供这些动态参数,Windows PowerShell 容器提供者必须实现 System.Management.Automation.Provider.ContainerCmdletProvider.RenameItemDynamicParameters* 方法。 该方法检索指定路径上的参数,返回一个具有属性和场的对象,解析属性类似于 cmdlet 类或 System.Management.Automation.RuntimeDefinedParameterDictionary 对象。 Windows PowerShell 运行时使用返回的对象将参数添加到 Rename-Item cmdlet。

该容器提供者未实现该方法。 不过,以下代码是该方法的默认实现。

创建新物品

要创建新项,容器提供者必须实现 System.Management.Automation.Provider.ContainerCmdletProvider.NewItem* 方法以支持该 New-Item 命令调用。 该方法会生成位于指定路径的数据项。 type cmdlet 的参数包含新项目的提供者定义类型。 例如,文件系统提供者使用 type 一个参数,取值为“file”或“directory”。 newItemValue cmdlet 的参数指定了新项目的提供者特定值。

这是该提供者系统管理自动化服务提供者. ContainerCmdletProvider.NewItem* 方法的实现。

protected override void NewItem( string path, string type, object newItemValue )
{
    // Create the new item here after
    // performing necessary validations
    //
    // WriteItemObject(newItemValue, path, false);

    // Example
    //
    // if (ShouldProcess(path, "new item"))
    // {
    //      // Create a new item and then call WriteObject
    //      WriteObject(newItemValue, path, false);
    // }

} // NewItem
{
    case 1:
        {
            string name = pathChunks[0];

            if (TableNameIsValid(name))
            {
                tableName = name;
                retVal = PathType.Table;
            }
        }
        break;

    case 2:
        {
            string name = pathChunks[0];

关于实施NewItem需要记住的事项

以下条件可能适用于您对 System.Management.Automation.Provider.ContainerCmdletProvider.NewItem*的实现:

将动态参数附加到 New-Item cmdlet

有时 New-Item cmdlet 需要在运行时动态指定额外参数。 为了提供这些动态参数,容器提供者必须实现 System.Management.Automation.Provider.ContainerCmdletProvider.NewItemDynamicParameters* 方法。 该方法检索指定路径上的参数,返回一个具有属性和场的对象,解析属性类似于 cmdlet 类或 System.Management.Automation.RuntimeDefinedParameterDictionary 对象。 Windows PowerShell 运行时使用返回的对象将参数添加到 New-Item cmdlet。

该提供者未实现该方法。 不过,以下代码是该方法的默认实现。

移除物品

要移除项目,Windows PowerShell 提供者必须覆盖 System.Management.Automation.Provider.ContainerCmdletProvider.RemoveItem* 方法,以支持从 cmdlet 调用 Remove-Item 。 该方法在指定路径处从数据存储中删除一个项目。 如果 recurseRemove-Item cmdlet 参数设置为 true,方法会删除所有子项,无论其级别如何。 如果参数设置为 false,方法只在指定路径上移除一个项目。

该服务商不支持物品移除。 然而,以下代码是 System.Management.Automation.Provider.ContainerCmdletProvider.RemoveItem*的默认实现。

关于实现RemoveItem需要记住的事项

以下条件可能适用于您对 System.Management.Automation.Provider.ContainerCmdletProvider.NewItem*的实现:

将动态参数附加到 Remove-Item cmdlet

有时 Remove-Item cmdlet 需要在运行时动态指定额外参数。 为了提供这些动态参数,容器提供者必须实现 System.Management.Automation.Provider.ContainerCmdletProvider.RemoveItemDynamicParameters* 方法来处理这些参数。 该方法检索指定路径上项目的动态参数,返回一个具有类似于 cmdlet 类或 System.Management.Automation.RuntimeDefinedParameterDictionary 对象的属性和字段的对象。 Windows PowerShell 运行时使用返回的对象将参数添加到 Remove-Item cmdlet。

该容器提供者未实现该方法。 然而,以下代码是 System.Management.Automation.Provider.ContainerCmdletProvider.RemoveItemDynamicParameters*的默认实现。

查询子项

为了检查指定路径是否存在子项,Windows PowerShell 容器提供者必须覆盖 System.Management.Automation.Provider.ContainerCmdletProvider.HasChildItems* 方法。 如果该项有子项,则返回该方法,false否则返回true。 对于空路径或空路径,方法将数据存储中的任意项视为子节点,返回 true

这是 System.Management.Automation.Provider.ContainerCmdletProvider.HasChildItems* 方法的覆盖。 如果 ChunkPath 辅助方法 false创建的路径部分超过两个,方法返回 ,因为只定义了一个数据库容器和一个表容器。 关于该辅助方法的更多信息,请参见 ChunkPath 方法,该方法在 《创建 Windows PowerShell 项提供者》中已有讨论。

protected override bool HasChildItems( string path )
{
    return false;
} // HasChildItems
        ErrorCategory.InvalidOperation, tableName));
}

return results;

关于实现 HasChildItems 需要注意的事项

以下条件可能适用于您对 System.Management.Automation.Provider.ContainerCmdletProvider.HasChildItems*的实现:

复制物品

要复制项目,容器提供者必须实现 System.Management.Automation.Provider.ContainerCmdletProvider.CopyItem 方法以支持该 Copy-Item 命令调用。 该方法将数据项从指令小子参数指示 path 的位置复制到参数指示 copyPath 的位置。 如果 recurse 参数被指定,方法会复制所有子容器。 如果参数未指定,方法只复制一层项。

该提供者未实现该方法。 然而,以下代码是 System.Management.Automation.Provider.ContainerCmdletProvider.CopyItem的默认实现。

实现CopyItem需要记住的事项

以下条件可能适用于您对 System.Management.Automation.Provider.ContainerCmdletProvider.CopyItem的实现:

将动态参数附加到 Copy-Item cmdlet

有时 Copy-Item cmdlet 需要在运行时动态指定额外参数。 为了提供这些动态参数,Windows PowerShell 容器提供者必须实现 System.Management.Automation.Provider.ContainerCmdletProvider.CopyItemDynamicParameters* 方法来处理这些参数。 该方法检索指定路径上的参数,返回一个具有属性和场的对象,解析属性类似于 cmdlet 类或 System.Management.Automation.RuntimeDefinedParameterDictionary 对象。 Windows PowerShell 运行时使用返回的对象将参数添加到 Copy-Item cmdlet。

该提供者未实现该方法。 然而,以下代码是 System.Management.Automation.Provider.ContainerCmdletProvider.CopyItemDynamicParameters*的默认实现。

代码示例

完整的示例代码,请参见 AccessDbProviderSample04 代码示例

构建 Windows PowerShell 提供者

请参阅 如何注册指令、提供者和托管应用程序

测试Windows PowerShell提供者

当你的 Windows PowerShell 提供者已注册 Windows PowerShell 后,可以通过命令行运行支持的 cmdlet 来测试。 请注意,以下示例输出使用了一个虚构的Access数据库。

  1. 运行 Get-ChildItem cmdlet 从 Access 数据库中的客户表中获取子项列表。

    Get-ChildItem mydb:customers
    

    以下是输出内容。

    PSPath        : AccessDB::customers
    PSDrive       : mydb
    PSProvider    : System.Management.Automation.ProviderInfo
    PSIsContainer : True
    Data          : System.Data.DataRow
    Name          : Customers
    RowCount      : 91
    Columns       :
    
  2. 再次运行 Get-ChildItem cmdlet 以获取表的数据。

    (Get-ChildItem mydb:customers).Data
    

    以下是输出内容。

    TABLE_CAT   : C:\PS\northwind
    TABLE_SCHEM :
    TABLE_NAME  : Customers
    TABLE_TYPE  : TABLE
    REMARKS     :
    
  3. 现在用 Get-Item cmdlet 在数据表的第 0 行检索这些项。

    Get-Item mydb:\customers\0
    

    以下是输出内容。

    PSPath        : AccessDB::customers\0
    PSDrive       : mydb
    PSProvider    : System.Management.Automation.ProviderInfo
    PSIsContainer : False
    Data          : System.Data.DataRow
    RowNumber     : 0
    
  4. 重用 Get-Item 以获取第0行项目的数据。

    (Get-Item mydb:\customers\0).Data
    

    以下是输出内容。

    CustomerID   : 1234
    CompanyName  : Fabrikam
    ContactName  : Eric Gruber
    ContactTitle : President
    Address      : 4567 Main Street
    City         : Buffalo
    Region       : NY
    PostalCode   : 98052
    Country      : USA
    Phone        : (425) 555-0100
    Fax          : (425) 555-0101
    
  5. 现在用 New-Item cmdlet 向已有的表添加一行。 该 Path 参数指定了该行的完整路径,并且必须表示行数大于表中现有行数。 参数 Type 表示 Row 要指定要添加的类型。 最后,参数 Value 指定了该行的逗号分隔列值列表。

    New-Item -Path mydb:\Customers\3 -ItemType "Row" -Value "3,CustomerFirstName,CustomerLastName,CustomerEmailAddress,CustomerTitle,CustomerCompany,CustomerPhone, CustomerAddress,CustomerCity,CustomerState,CustomerZip,CustomerCountry"
    
  6. 验证新项目作的正确性如下。

    PS mydb:\> cd Customers
    PS mydb:\Customers> (Get-Item 3).Data
    

    以下是输出内容。

    ID        : 3
    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
    

另请参阅

创建 Windows PowerShell 提供者

设计您的Windows PowerShell提供者

实现一个项目 Windows PowerShell Provider

实现导航 Windows PowerShell 提供者

如何注册 Cmdlet、提供者和托管应用程序

Windows PowerShell SDK

Windows PowerShell 程序员指南