本主題說明如何建立一個 Windows PowerShell 提供者,讓使用者能操作資料儲存中項目的內容。 因此,能夠操作項目內容的提供者稱為 Windows PowerShell 內容提供者。
備註
你可以使用 Microsoft Windows 軟體開發套件(Windows Vista 及 .NET Framework 3.0 執行時元件)下載 C# 原始碼檔案(AccessDBSampleProvider06.cs)。 如需下載說明,請參閱 《如何安裝 Windows PowerShell》及《Download the 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 的內容閱讀類別。 此提供者的內容讀取器允許存取資料表中某一列的內容。 內容讀取類別 定義了讀取 方法,從指定列檢索資料並回傳代表該資料的清單;一個 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 方法(移動內容撰寫者)、一個關閉(關閉內容撰寫者)的方法,以及一個處置方法。
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 指令小子
Get-Content指令長可能會在執行時動態指定額外參數。 為了提供這些動態參數,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 列舉中的 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 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 列舉中的 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 指令小子
Clear-Content指令檔可能需要執行時新增的動態參數。 為了提供這些動態參數,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 後,你可以在命令列執行支援的 cmdlets 來測試。 例如,測試範例內容提供者。
使用 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