本主题描述了如何创建一个 Windows PowerShell 提供者,使用户能够作数据存储中项目的内容。 因此,能够作项目内容的提供者被称为Windows PowerShell内容提供者。
注释
您可以使用 Windows Vista 和 .NET Framework 3.0 运行时组件Microsoft Windows 软件开发工具包下载该提供者的 C# 源文件(AccessDBSampleProvider06.cs)。 有关下载说明,请参见 《如何安装Windows PowerShell》和《Windows PowerShell SDK》。 下载的源文件可在 <PowerShell Samples> 目录中获取。 有关其他 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 的内容阅读类。 该提供者的内容阅读器允许访问数据表中某行的内容。 内容阅读类定义了读取方法,用于从指定行检索数据并返回表示该数据的列表;一个Seek方法,用于移动内容阅读器;一个关闭方法,关闭阅读器;以及一个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 的内容编写类。 内容写作者类 定义了写 入方法(写入指定行内容)、 Seeek 方法(移动内容写作者)、关闭内容写作者的 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 命令。 该方法返回位于指定路径的项目内容阅读器。 然后可以打开阅读对象来读取内容。
这是该提供者该方法中 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*的实现:
在定义提供者类时,Windows PowerShell 内容提供者可能会声明从 System.Management.Automation.Provider.ProviderCapabilities 枚举中声明 ExpandWildcard、Filter、Include 或 Exclude 的提供者能力。 在这些情况下, System.Management.Automation.Provider.IContentCmdletProvider.GetContentReader* 方法的实现必须确保传递给该方法的路径满足指定能力的要求。 为此,方法应访问相应属性,例如 System.Management.Automation.Provider.CmdletProvider.Exclude* 和 System.Management.Automation.Provider.CmdletProvider.Include* 属性。
默认情况下,除非将 System.Management.Automation.Provider.CmdletProvider.Force* 属性设置为
true,否则该方法的覆盖不应检索隐藏对象的读取器。 如果路径代表对用户隐藏的项目,且 System.Management.Automation.Provider.CmdletProvider.Force* 设置为false,则应写入错误。
将动态参数附加到 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-Content 和 Add-Content 命令列表。 该方法返回位于指定路径的项目的内容编写者。
这是 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*:
在定义提供者类时,Windows PowerShell 内容提供者可能会声明从 System.Management.Automation.Provider.ProviderCapabilities 枚举中声明 ExpandWildcard、Filter、Include 或 Exclude 的提供者能力。 在这种情况下, System.Management.Automation.Provider.IContentCmdletProvider.GetContentWriter* 方法的实现必须确保传递给该方法的路径满足指定能力的要求。 为此,方法应访问相应属性,例如 System.Management.Automation.Provider.CmdletProvider.Exclude* 和 System.Management.Automation.Provider.CmdletProvider.Include* 属性。
默认情况下,除非将 System.Management.Automation.Provider.CmdletProvider.Force* 属性设置为
true。 如果路径代表对用户隐藏的项目,且 System.Management.Automation.Provider.CmdletProvider.Force* 设置为false,则应写入错误。
将动态参数附加到 Add-Content 和 Set-Content 指令小子上
Add-Content和 Set-Content cmdlets 可能需要额外的动态参数,这些参数会在运行时中添加。 为了提供这些动态参数,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 命令。 该方法移除指定路径上的内容,但保持该项目完整。
以下是该提供者系统 管理.自动化.提供者.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*的实现:
在定义提供者类时,Windows PowerShell 内容提供者可能会声明从 System.Management.Automation.Provider.ProviderCapabilities 枚举中声明 ExpandWildcard、Filter、Include 或 Exclude 的提供者能力。 在这种情况下, System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* 方法的实现必须确保传递给该方法的路径满足指定能力的要求。 为此,方法应访问相应属性,例如 System.Management.Automation.Provider.CmdletProvider.Exclude* 和 System.Management.Automation.Provider.CmdletProvider.Include* 属性。
默认情况下,除非将 System.Management.Automation.Provider.CmdletProvider.Force* 属性设置为
true,否则该方法的覆盖不应清除对用户隐藏的对象内容。 如果路径代表对用户隐藏的项目,且 System.Management.Automation.Provider.CmdletProvider.Force* 设置为false,则应写入错误。你实现 System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* 方法时,应调用 System.Management.Automation.Provider.CmdletProvider.ShouldProcess 并验证其返回值,然后再对数据存储进行任何更改。 该方法用于在数据存储发生变更(如清除内容)时确认作的执行。 System.Management.Automation.Provider.CmdletProvider.ShouldProcess 方法将要更改的资源名称发送给用户,Windows PowerShell 运行时负责命令行设置或偏好变量,决定应显示的内容。
在调用 System.Management.Automation.Provider.CmdletProvider.ShouldProcess 返回
true后, System.Management.Automation.Provider.IContentCmdletProvider.ClearContent* 方法应调用 System.Management.Automation.Provider.CmdletProvider.ShouldContinue 方法。 该方法向用户发送消息,允许反馈以验证作是否应继续。 调用 System.Management.Automation.Provider.CmdletProvider.ShouldContinue 可以额外检查潜在危险的系统修改。
将动态参数附加到 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 可以用来识别对象的成员,以及一个定义对象显示方式的 Format 文件。 更多信息请参见 “扩展对象类型与格式”。
构建 Windows PowerShell 提供者
请参阅 如何注册指令、提供者和托管应用程序。
测试Windows PowerShell提供者
当你的 Windows PowerShell 提供者已注册 Windows PowerShell 后,可以通过命令行运行支持的 cmdlet 来测试。 例如,测试示例内容提供者。
使用 Get-Content 该 cmdlet 在参数指定的 Path 路径上检索数据库表中指定项目的内容。 该 ReadCount 参数指定定义内容读取器可读取的项目数量(默认为1)。 通过以下命令条目,命令小工具从表中检索两行(项)并显示其内容。 请注意,以下示例输出使用了一个虚构的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