本主題描述如何建立 Windows PowerShell 提供者,讓用戶能夠作數據存放區中項目的內容。 因此,可以作項目內容的提供者稱為 Windows PowerShell 內容提供者。
備註
您可以使用適用於 Windows Vista 的 Microsoft Windows 軟體開發工具包和 .NET Framework 3.0 運行時間元件,下載此提供者的 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*的實作:
定義提供者類別時,Windows PowerShell 內容提供者可能會從 System.Management.Automation.Provider.ProviderCapabilities 列舉宣告 ExpandWildcards、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
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*:
定義提供者類別時,Windows PowerShell 內容提供者可能會從 System.Management.Automation.Provider.ProviderCapabilities 列舉宣告 ExpandWildcards、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 Cmdlet
Add-Content
和 Set-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*的實作:
定義提供者類別時,Windows PowerShell 內容提供者可能會從 System.Management.Automation.Provider.ProviderCapabilities 列舉宣告 ExpandWildcards、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 程式代碼範例。
定義物件類型和格式設定
撰寫提供者時,可能需要將成員新增至現有物件或定義新的物件。 完成此動作時,您必須建立 Windows PowerShell 可用來識別物件成員的 Types 檔案,以及定義物件顯示方式的 Format 檔案。 如需詳細資訊,請參閱 擴充物件類型和格式設定。
建置 Windows PowerShell 提供者
測試 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