The second CTP of PowerShell V2 (CTP2) introduces full engine support for transactions – groups of actions that can be finalized or undone in an all-or-nothing way. Wikipedia gives a great overview of transactions here: http://en.wikipedia.org/wiki/Database_transactions.
We put a ton of thought into how to expose this normally developer-centric concept in a way suitable for system administration and system administrators. We would LOVE to hear your feedback on what concepts or behaviours make sense, and especially which ones do not.
We didn’t get a chance to fully document these cmdlets in the CTP2, though, so here’s a quick start and primer to help you explore the feature.
Using transactions
PowerShell surfaces its support for transactions through the following cmdlets:
PS C:\Temp> gcm *transaction*
CommandType Name
———– —-
Cmdlet Complete-PSTransaction
Completes / Commits a transaction
Cmdlet Start-PSTransaction
Begins a transaction
Cmdlet Undo-PSTransaction
Rolls back a transaction
Cmdlet Use-PSTransaction
Places the current PowerShell transaction in Transaction.Current, for direct .NET Scripting against transacted objects.
To start a transaction, call the Start-PSTransaction cmdlet. To use a cmdlet that supports transactions, call it with the –UseTransaction parameter. Being explicit about this parameter is crucial, as many cmdlets that support transactions can work equally well without one. Because of that, PowerShell only surfaces the transaction to the cmdlet when you supply this parameter.
PowerShell’s registry provider supports transactions on Vista. In addition, a utility class called System.Management.Automation.TransactedString supports transactions on all platforms.
Once you have completed the transactional work, call the Complete-PSTransaction cmdlet to make it final, or the Undo-PSTransaction cmdlet to discard the changes.
Here is an example session that illustrates these concepts:
PS C:\Users\leeholm> cd hkcu:\temp
PS HKCU:\temp> dir
PS HKCU:\temp> Start-PsTransaction
## Create a key. We didn’t specify the –UseTransaction parameter, so it is not being done in a transaction.
PS HKCU:\temp> New-Item WillStayBehind
Hive: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\temp
SKC VC Name Property
— — —- ——–
0 0 WillStayBehind {}
## Create a key in the transaction
PS HKCU:\temp> New-Item WillGetRolledBack -UseTransaction
Hive: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\temp
SKC VC Name Property
— — —- ——–
0 0 WillGetRolledBack {}
## Get-ChildItem from outside of the transaction. You don’t see any of your transacted changes.
PS HKCU:\temp> Get-ChildItem
Hive: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\temp
SKC VC Name Property
— — —- ——–
0 0 WillStayBehind {}
## Get-ChildItem from inside of the transaction. Your transacted changes are now visible.
PS HKCU:\temp> Get-ChildItem -UseTransaction
Hive: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\temp
SKC VC Name Property
— — —- ——–
0 0 WillStayBehind {}
0 0 WillGetRolledBack {}
## Now, some transacted .NET scripting against the TransactedString object
PS HKCU:\temp> $transactedString = New-Object System.Management.Automation.TransactedString
PS HKCU:\temp> $transactedString.Append(“Hello “)
## Append some text in the transaction.
PS HKCU:\temp> Use-PsTransaction -UseTransaction { $transactedString.Append(“World”) }
## From outside the transaction, the changes are not visible
PS HKCU:\temp> $transactedString.ToString()
Hello
## But from within the transaction, they are
PS HKCU:\temp> Use-PsTransaction -UseTransaction { $transactedString.ToString() }
Hello World
## Roll back the transaction
PS HKCU:\temp> Undo-PsTransaction
## Look at the registry, and only our non-transacted changes are there.
PS HKCU:\temp> Get-ChildItem
Hive: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\temp
SKC VC Name Property
— — —- ——–
0 0 WillStayBehind {}
PS HKCU:\temp> Get-ChildItem -UseTransaction
Get-ChildItem : Cannot use transaction. No transaction has been started.
At line:1 char:14
+ Get-ChildItem <<<< -UseTransaction
PS HKCU:\temp> $transactedString.ToString()
Hello
PS HKCU:\temp>
Developing for transactions – Cmdlet and Provider declarations
Cmdlets declare their support for transactions in a similar way that they declare their support for ShouldProcess:
[Cmdlet(“Get”, “Process”, SupportsTransactions=True)]
Providers declare their support for transactions through the ProviderCapabilities flag:
[CmdletProvider(“Registry”, ProviderCapabilities.Transactions)]
Developing for transactions – Participating in the PowerShell transaction
The PowerShell engine exposes transactions in a way that lets developers implement a transacted provider or cmdlet in the same way that they implement other transacted code.
The recommended pattern for developing isolated transacted code in traditional applications is this: http://msdn2.microsoft.com/en-us/library/ms229973.aspx
using(TransactionScope scope = new TransactionScope())
{
… // Perform transactional work here
// No errors – commit transaction
scope.Complete();
}
This is called implicit transaction management, and the code in the code block represents the entirety of the transacted operation. The TransactionScope object handles management of the ambient transaction, committing and rolling it back as necessary.
Since cmdlet and provider developers should not deal directly with transaction scopes (but still be able to easily port transacted code,) PowerShell gives a similar experience:
using(CurrentPsTransaction)
{
... // Perform transactional work here
}
The CurrentPsTransaction property returns an IDisposable object. This IDisposable object, through the System.Transactions explicit programming model (http://msdn2.microsoft.com/en-us/library/ms172146.aspx), sets the current ambient transaction to be the current PowerShell Transaction. Its dispose method restores the previous ambient transaction. Attempting to use this current transaction while no transactions are active generates an error. The other transaction cmdlets control the nesting and lifetime of these transactions.
This CurrentPsTransaction experience differs from the C# TransactionScope experience in two ways:
1) The object returned by the CurrentPsTransaction property does not actually represent a transaction. It does not support a Complete() method, Rollback() method, or anything else. The user is in charge of these decisions, not the cmdlet or provider developer.
2) The transaction persists beyond the end of the code block, as multiple cmdlets can participate in it. Although the transaction persists outside of the code block, it is not active / ambient outside of the code block. A cmdlet or provider author can easily write the following code to tightly control which operations are transacted:
using(CurrentPsTransaction)
{
... // Perform transactional work here
}
... // Perform non-transacted work here
using(CurrentPsTransaction)
{
... // Perform more transactional work here
}
Using the same pattern PowerShell has established for the ShouldProcess functionality, cmdlet and provider authors should check for an existing transaction if they can operate without one:
if(TransactionAvailable())
{
using(CurrentPsTransaction)
{
... // Perform transactional work here
}
}
If the cmdlet or provider cannot operate without a transaction, they can simply use the CurrentPsTransaction property. If no transaction is available, PowerShell automatically generates an error.
—
Lee Holmes [MSFT]
Windows PowerShell Development
0 comments