コンテナー プロバイダーを記述する
このトピックでは、ファイル システム プロバイダー内のフォルダーなど、他の項目を含む項目をサポートする Windows PowerShell プロバイダーのメソッドを実装する方法について説明します。 コンテナーをサポートするには、プロバイダーが System.Management.Automation.Provider.Containercmdletprovider クラスから派生している必要 があります。
このトピックの例のプロバイダーでは、Access データベースをデータ ストアとして使用します。 データベースとの対話に使用されるヘルパー メソッドとクラスがいくつかあります。 ヘルパー メソッドを含む完全なサンプルについては 、「AccessDBProviderSample04 」を参照してください。
プロバイダーの詳細については、「Windows PowerShellプロバイダーの概要」Windows PowerShell参照してください。
コンテナー メソッドの実装
System.Management.Automation.Provider.Containercmdletproviderクラスは、コンテナーをサポートし、項目を作成、コピー、および削除するメソッドを実装します。 これらのメソッドの完全な一覧については 、「System.Management.Automation.Provider.ContainerCmdletProvider 」を参照してください。
注意
このトピックは、「プロバイダーのクイック スタート」のWindows PowerShell基づいています。 このトピックでは、プロバイダー プロジェクトを設定する方法の基本、またはドライブを作成および削除する System.Management.Automation.Provider.Drivecmdletprovider クラスから継承されたメソッドを実装する方法については説明します。 このトピックでは 、System.Management.Automation.Provider.Itemcmdletprovider クラスによって公開されるメソッドを実装する方法も説明します。 項目コマンドレットを実装する方法を示す例については、「項目プロバイダーの作成 」を参照してください。
プロバイダー クラスの宣言
System.Management.Automation.Provider.Containercmdletproviderクラスから派生するプロバイダーを宣言し、System.Management.Automation.Provider.Cmdletproviderattributeで装飾します。
[CmdletProvider("AccessDB", ProviderCapabilities.None)]
public class AccessDBProvider : ContainerCmdletProvider
{
}
GetChildItems の実装
ユーザーがMicrosoft.PowerShell.Commands.GetChildItemCommandコマンドレットを呼び出す場合、PowerShell エンジンはSystem.Management.Automation.Provider.Containercmdletprovider.Getchilditems*メソッドを呼び出します。 このメソッドは、指定したパスにある項目の子である項目を取得します。
Access データベースの例では 、System.Management.Automation.Provider.Containercmdletprovider.Getchilditems* メソッドの動作は、指定された項目の種類によって異なります。 項目がドライブの場合、子はテーブルであり、 メソッドはデータベースからテーブルのセットを返します。 指定した項目がテーブルの場合、子はそのテーブルの行になります。 項目が行の場合、子は含め "いません"。このメソッドは、その行のみを返します。 すべての子項目は 、System.Management.Automation.Provider.Cmdletprovider.Writeitemobject* メソッドによって PowerShell エンジンに送り返 されます。
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
}
GetChildNames の実装
System.Management.Automation.Provider.Containercmdletprovider.Getchildnames*メソッドはSystem.Management.Automation.Provider.Containercmdletprovider.Getchilditems*メソッドに似ていますが、項目自体ではなく項目の name プロパティのみを返す点が除いてあります。
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
}
NewItem の実装
System.Management.Automation.Provider.Containercmdletprovider.Newitem*メソッドは、指定したパスに指定した型の新しい項目を作成します。 ユーザーが Microsoft.PowerShell.Commands.NewItemCommand コマンドレットを呼び出す場合、PowerShell エンジンは、このメソッドを呼び出します。
この例では、 メソッドは、パスと型が一致すると判断するロジックを実装します。 つまり、ドライブ (データベース) の直下にテーブルのみを作成し、テーブルの下に 1 行のみを作成できます。 指定したパスと項目の種類がこの方法で一致しない場合、メソッドは例外をスローします。
protected override void NewItem(string path, string type,
object newItemValue)
{
string tableName;
int rowNumber;
PathType pt = GetNamesFromPath(path, out tableName, out rowNumber);
if (pt == PathType.Invalid)
{
ThrowTerminatingInvalidPathException(path);
}
// Check if type is either "table" or "row", if not throw an
// exception
if (!String.Equals(type, "table", StringComparison.OrdinalIgnoreCase)
&& !String.Equals(type, "row", StringComparison.OrdinalIgnoreCase))
{
WriteError(new ErrorRecord
(new ArgumentException("Type must be either a table or row"),
"CannotCreateSpecifiedObject",
ErrorCategory.InvalidArgument,
path
)
);
throw new ArgumentException("This provider can only create items of type \"table\" or \"row\"");
}
// Path type is the type of path of the container. So if a drive
// is specified, then a table can be created under it and if a table
// is specified, then a row can be created under it. For the sake of
// completeness, if a row is specified, then if the row specified by
// the path does not exist, a new row is created. However, the row
// number may not match as the row numbers only get incremented based
// on the number of rows
if (PathIsDrive(path))
{
if (String.Equals(type, "table", StringComparison.OrdinalIgnoreCase))
{
// Execute command using ODBC connection to create a table
try
{
// create the table using an sql statement
string newTableName = newItemValue.ToString();
if (!TableNameIsValid(newTableName))
{
return;
}
string sql = "create table " + newTableName
+ " (ID INT)";
// Create the table using the Odbc connection from the
// drive.
AccessDBPSDriveInfo di = this.PSDriveInfo as AccessDBPSDriveInfo;
if (di == null)
{
return;
}
OdbcConnection connection = di.Connection;
if (ShouldProcess(newTableName, "create"))
{
OdbcCommand cmd = new OdbcCommand(sql, connection);
cmd.ExecuteScalar();
}
}
catch (Exception ex)
{
WriteError(new ErrorRecord(ex, "CannotCreateSpecifiedTable",
ErrorCategory.InvalidOperation, path)
);
}
} // if (String...
else if (String.Equals(type, "row", StringComparison.OrdinalIgnoreCase))
{
throw new
ArgumentException("A row cannot be created under a database, specify a path that represents a Table");
}
}// if (PathIsDrive...
else
{
if (String.Equals(type, "table", StringComparison.OrdinalIgnoreCase))
{
if (rowNumber < 0)
{
throw new
ArgumentException("A table cannot be created within another table, specify a path that represents a database");
}
else
{
throw new
ArgumentException("A table cannot be created inside a row, specify a path that represents a database");
}
} //if (String.Equals....
// if path specified is a row, create a new row
else if (String.Equals(type, "row", StringComparison.OrdinalIgnoreCase))
{
// The user is required to specify the values to be inserted
// into the table in a single string separated by commas
string value = newItemValue as string;
if (String.IsNullOrEmpty(value))
{
throw new
ArgumentException("Value argument must have comma separated values of each column in a row");
}
string[] rowValues = value.Split(',');
OdbcDataAdapter da = GetAdapterForTable(tableName);
if (da == null)
{
return;
}
DataSet ds = GetDataSetForTable(da, tableName);
DataTable table = GetDataTable(ds, tableName);
if (rowValues.Length != table.Columns.Count)
{
string message =
String.Format(CultureInfo.CurrentCulture,
"The table has {0} columns and the value specified must have so many comma separated values",
table.Columns.Count);
throw new ArgumentException(message);
}
if (!Force && (rowNumber >=0 && rowNumber < table.Rows.Count))
{
string message = String.Format(CultureInfo.CurrentCulture,
"The row {0} already exists. To create a new row specify row number as {1}, or specify path to a table, or use the -Force parameter",
rowNumber, table.Rows.Count);
throw new ArgumentException(message);
}
if (rowNumber > table.Rows.Count)
{
string message = String.Format(CultureInfo.CurrentCulture,
"To create a new row specify row number as {0}, or specify path to a table",
table.Rows.Count);
throw new ArgumentException(message);
}
// Create a new row and update the row with the input
// provided by the user
DataRow row = table.NewRow();
for (int i = 0; i < rowValues.Length; i++)
{
row[i] = rowValues[i];
}
table.Rows.Add(row);
if (ShouldProcess(tableName, "update rows"))
{
// Update the table from memory back to the data source
da.Update(ds, tableName);
}
}// else if (String...
}// else ...
}
CopyItem の実装
System.Management.Automation.Provider.ContainerCmdletProvider.CopyItem は、指定した項目を指定したパスにコピーします。 ユーザーが Microsoft.PowerShell.Commands.CopyItemCommand コマンドレットを呼び出す場合、PowerShell エンジンは、このメソッドを呼び出します。 このメソッドは再帰的に指定し、項目自体に加えてすべての項目の子をコピーできます。
System.Management.Automation.Provider.Containercmdletprovider.Newitem*メソッドと同様に、このメソッドはロジックを実行して、指定された項目がコピーされるパスに対して正しい型である必要があります。 たとえば、コピー先パスがテーブルの場合、コピーする項目は行である必要があります。
protected override void CopyItem(string path, string copyPath, bool recurse)
{
string tableName, copyTableName;
int rowNumber, copyRowNumber;
PathType type = GetNamesFromPath(path, out tableName, out rowNumber);
PathType copyType = GetNamesFromPath(copyPath, out copyTableName, out copyRowNumber);
if (type == PathType.Invalid)
{
ThrowTerminatingInvalidPathException(path);
}
if (type == PathType.Invalid)
{
ThrowTerminatingInvalidPathException(copyPath);
}
// Get the table and the table to copy to
OdbcDataAdapter da = GetAdapterForTable(tableName);
if (da == null)
{
return;
}
DataSet ds = GetDataSetForTable(da, tableName);
DataTable table = GetDataTable(ds, tableName);
OdbcDataAdapter cda = GetAdapterForTable(copyTableName);
if (cda == null)
{
return;
}
DataSet cds = GetDataSetForTable(cda, copyTableName);
DataTable copyTable = GetDataTable(cds, copyTableName);
// if source represents a table
if (type == PathType.Table)
{
// if copyPath does not represent a table
if (copyType != PathType.Table)
{
ArgumentException e = new ArgumentException("Table can only be copied on to another table location");
WriteError(new ErrorRecord(e, "PathNotValid",
ErrorCategory.InvalidArgument, copyPath));
throw e;
}
// if table already exists then force parameter should be set
// to force a copy
if (!Force && GetTable(copyTableName) != null)
{
throw new ArgumentException("Specified path already exists");
}
for (int i = 0; i < table.Rows.Count; i++)
{
DataRow row = table.Rows[i];
DataRow copyRow = copyTable.NewRow();
copyRow.ItemArray = row.ItemArray;
copyTable.Rows.Add(copyRow);
}
} // if (type == ...
// if source represents a row
else
{
if (copyType == PathType.Row)
{
if (!Force && (copyRowNumber < copyTable.Rows.Count))
{
throw new ArgumentException("Specified path already exists.");
}
DataRow row = table.Rows[rowNumber];
DataRow copyRow = null;
if (copyRowNumber < copyTable.Rows.Count)
{
// copy to an existing row
copyRow = copyTable.Rows[copyRowNumber];
copyRow.ItemArray = row.ItemArray;
copyRow[0] = GetNextID(copyTable);
}
else if (copyRowNumber == copyTable.Rows.Count)
{
// copy to the next row in the table that will
// be created
copyRow = copyTable.NewRow();
copyRow.ItemArray = row.ItemArray;
copyRow[0] = GetNextID(copyTable);
copyTable.Rows.Add(copyRow);
}
else
{
// attempting to copy to a nonexistent row or a row
// that cannot be created now - throw an exception
string message = String.Format(CultureInfo.CurrentCulture,
"The item cannot be specified to the copied row. Specify row number as {0}, or specify a path to the table.",
table.Rows.Count);
throw new ArgumentException(message);
}
}
else
{
// destination path specified represents a table,
// create a new row and copy the item
DataRow copyRow = copyTable.NewRow();
copyRow.ItemArray = table.Rows[rowNumber].ItemArray;
copyRow[0] = GetNextID(copyTable);
copyTable.Rows.Add(copyRow);
}
}
if (ShouldProcess(copyTableName, "CopyItems"))
{
cda.Update(cds, copyTableName);
}
} //CopyItem
RemoveItem の実装
System.Management.Automation.Provider.Containercmdletprovider.Removeitem*メソッドは、指定されたパスにある項目を削除します。 ユーザーが Microsoft.PowerShell.Commands.RemoveItemCommand コマンドレットを呼び出す場合、PowerShell エンジンは、このメソッドを呼び出します。
protected override void RemoveItem(string path, bool recurse)
{
string tableName;
int rowNumber = 0;
PathType type = GetNamesFromPath(path, out tableName, out rowNumber);
if (type == PathType.Table)
{
// if recurse flag has been specified, delete all the rows as well
if (recurse)
{
OdbcDataAdapter da = GetAdapterForTable(tableName);
if (da == null)
{
return;
}
DataSet ds = GetDataSetForTable(da, tableName);
DataTable table = GetDataTable(ds, tableName);
for (int i = 0; i < table.Rows.Count; i++)
{
table.Rows[i].Delete();
}
if (ShouldProcess(path, "RemoveItem"))
{
da.Update(ds, tableName);
RemoveTable(tableName);
}
}//if (recurse...
else
{
// Remove the table
if (ShouldProcess(path, "RemoveItem"))
{
RemoveTable(tableName);
}
}
}
else if (type == PathType.Row)
{
OdbcDataAdapter da = GetAdapterForTable(tableName);
if (da == null)
{
return;
}
DataSet ds = GetDataSetForTable(da, tableName);
DataTable table = GetDataTable(ds, tableName);
table.Rows[rowNumber].Delete();
if (ShouldProcess(path, "RemoveItem"))
{
da.Update(ds, tableName);
}
}
else
{
ThrowTerminatingInvalidPathException(path);
}
}
次のステップ
一般的な現実世界のプロバイダーは、ドライブ内のあるパスから別のパスに項目を移動できます。 項目の移動をサポートするプロバイダーの例については、「ナビゲーション プロバイダーの作成 」を参照してください。
参照
フィードバック
フィードバックの送信と表示