Een containerprovider schrijven
In dit onderwerp wordt beschreven hoe u de methoden implementeert van een Windows PowerShell-provider die ondersteuning biedt voor items die andere items bevatten, zoals mappen in de bestandssysteemprovider. Om containers te kunnen ondersteunen, moet een provider zijn afgeleid van de klasse System.Management.Automation.Provider.Containercmdletprovider.
De provider in de voorbeelden in dit onderwerp gebruikt een Access-database als gegevensopslag. Er zijn verschillende helpermethoden en -klassen die worden gebruikt voor interactie met de database. Zie AccessDBProviderSample04voor het volledige voorbeeld met de helpermethoden.
Zie voor meer informatie Windows PowerShell providers Windows PowerShell Provider Overview.
Containermethoden implementeren
De klasse System.Management.Automation.Provider.Containercmdletprovider implementeert methoden die ondersteuning bieden voor containers en het maken, kopiƫren en verwijderen van items. Zie System.Management.Automation.Provider.ContainerCmdletProvidervoor een volledige lijst van deze methoden.
Notitie
Dit onderwerp bouwt voort op de informatie in Windows PowerShell Provider QuickStart. In dit onderwerp worden niet de basisbeginselen besproken van het instellen van een providerproject of het implementeren van de methoden die zijn overgenomen van de klasse System.Management.Automation.Provider.Drivecmdletprovider voor het maken en verwijderen van stations. In dit onderwerp wordt ook niet besproken hoe u methoden implementeert die worden blootgesteld door de klasse System.Management.Automation.Provider.Itemcmdletprovider. Zie Een itemproviderschrijven voor een voorbeeld van het implementeren van item-cmdlets.
De providerklasse declareren
Declareer de provider om af te leiden van de klasse System.Management.Automation.Provider.Containercmdletprovider en versier deze met de klasse System.Management.Automation.Provider.Cmdletproviderattribute.
[CmdletProvider("AccessDB", ProviderCapabilities.None)]
public class AccessDBProvider : ContainerCmdletProvider
{
}
GetChildItems implementeren
De PowerShell-engine roept de methode System.Management.Automation.Provider.Containercmdletprovider.Getchilditems* aan wanneer een gebruiker de cmdlet Microsoft.PowerShell.Commands.GetChildItemCommand aanroept. Met deze methode worden de items opgeslagen die de kinderen van het item zijn op het opgegeven pad.
In het access-databasevoorbeeld is het gedrag van de methode System.Management.Automation.Provider.Containercmdletprovider.Getchilditems* afhankelijk van het type van het opgegeven item. Als het item het station is, zijn de kinderen tabellen en retourneert de methode de set tabellen uit de database. Als het opgegeven item een tabel is, zijn de kinderen de rijen van die tabel. Als het item een rij is, heeft het geen kinderen en retourneert de methode alleen die rij. Alle onderliggende items worden teruggestuurd naar de PowerShell-engine met de methode System.Management.Automation.Provider.Cmdletprovider.Writeitemobject*.
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 implementeren
De methode System.Management.Automation.Provider.Containercmdletprovider.Getchildnames* is vergelijkbaar met de methode System.Management.Automation.Provider.Containercmdletprovider.Getchilditems*, behalve dat alleen de naam-eigenschap van de items en niet de items zelf wordt retourneert.
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 implementeren
De methode System.Management.Automation.Provider.Containercmdletprovider.Newitem* maakt een nieuw item van het opgegeven type op het opgegeven pad. De PowerShell-engine roept deze methode aan wanneer een gebruiker de cmdlet Microsoft.PowerShell.Commands.NewItemCommand aanroept.
In dit voorbeeld implementeert de methode logica om te bepalen of het pad en type overeenkomen. Dat wil zeggen dat alleen een tabel rechtstreeks onder het station (de database) kan worden gemaakt en alleen een rij onder een tabel kan worden gemaakt. Als het opgegeven pad en het opgegeven itemtype niet op deze manier overeenkomen, wordt door de methode een uitzondering gemaakt.
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 implementeren
System.Management.Automation.Provider.ContainerCmdletProvider.CopyItem kopieert het opgegeven item naar het opgegeven pad. De PowerShell-engine roept deze methode aan wanneer een gebruiker de cmdlet Microsoft.PowerShell.Commands.CopyItemCommand aanroept. Deze methode kan ook recursief zijn en alle items naast het item zelf kopiƫren.
Net als bij de methode System.Management.Automation.Provider.Containercmdletprovider.Newitem* voert deze methode logica uit om ervoor te zorgen dat het opgegeven item van het juiste type is voor het pad waar het naar wordt gekopieerd. Als het doelpad bijvoorbeeld een tabel is, moet het item dat moet worden gekopieerd, een rij zijn.
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 implementeren
De methode System.Management.Automation.Provider.Containercmdletprovider.Removeitem* verwijdert het item op het opgegeven pad. De PowerShell-engine roept deze methode aan wanneer een gebruiker de cmdlet Microsoft.PowerShell.Commands.RemoveItemCommand aanroept.
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);
}
}
Volgende stappen
Een typische provider in de echte wereld is geschikt voor het verplaatsen van items van het ene pad naar het andere binnen het station. Zie Een navigatieprovider schrijven voor een voorbeeld van een provider die ondersteuning biedt voor het verplaatsen van items.
Zie ook
Feedback
Feedback verzenden en weergeven voor