我正在用连接数据库的C#编写PowerShell模块。该模块具有Get-MyDatabaseRecord
cmdlet,可用于查询数据库。如果变量PSCredential
中有$MyCredentials
个对象,则可以像这样调用cmdlet:
PS C:\> Get-MyDatabaseRecord -Credential $MyCredentials -Id 3
MyRecordId : 3
MyRecordValue : test_value
问题是,每次调用Credential
时都必须指定Get-MyDatabaseRecord
参数是繁琐且低效的。如果你可以只调用一个cmdlet连接到数据库,然后再调用另一个cmdlet来获取记录,那会更好:
PS C:\> Connect-MyDatabase -Credential $MyCredentials
PS C:\> Get-MyDatabaseRecord -Id 3
MyRecordId : 3
MyRecordValue : test_value
为了实现这一点,Connect-MyDatabase
cmdlet必须将数据库连接对象存储在某处,以便Get-MyDatabaseRecord
cmdlet可以获取该对象。我该怎么做?
我可以在某处定义一个静态变量来包含数据库连接:
static class ModuleState
{
internal static IDbConnection CurrentConnection { get; set; }
}
然而,全球可变状态通常是一个坏主意。这会以某种方式引起问题,还是这是一个很好的解决方案?
(问题的一个例子是,如果多个PowerShell会话以某种方式共享我的程序集的相同实例。那么所有会话都会无意中共享一个CurrentConnection
属性。但我不知道是否这实际上是可能的。)
MSDN页面" Windows PowerShell Session State"谈论会话状态的事情。该页面显示"会话状态数据"包含"会话状态变量信息",但它没有详细说明这些信息是什么或如何访问它。
该页面还说the SessionState
class可用于访问会话状态数据。此类包含名为PSVariable
的属性,类型为PSVariableIntrinsics
。
但是,我有两个问题。第一个问题是访问SessionState
属性要求我继承PSCmdlet
而不是Cmdlet
,我不确定是否要这样做。
第二个问题是我无法弄清楚如何将变量设为私有。这是我尝试的代码:
const int TestVariableDefault = 10;
const string TestVariableName = "TestVariable";
int TestVariable
{
get
{
return (int)SessionState.PSVariable.GetValue(TestVariableName,
TestVariableDefault);
}
set
{
PSVariable testVariable = new PSVariable(TestVariableName, value,
ScopedItemOptions.Private);
SessionState.PSVariable.Set(testVariable);
}
}
TestVariable
属性正如我所期望的那样工作。但是,尽管我使用ScopedItemOptions.Private
,我仍然可以通过输入$TestVariable
在提示符下访问此变量,并且变量列在Get-Variable
的输出中。我希望我的变量对用户隐藏。
答案 0 :(得分:6)
一种方法是使用输出连接对象的cmdlet或函数。此对象可以只是PSCredential对象,也可以包含凭据和连接字符串等其他信息。您现在将其保存在变量中并且可以继续执行此操作,但您也可以使用$ PSDefaultParamterValues存储此值并将其传递给模块中的所有相应cmdlet。
我从未写过C#模块,但我在PS中做过类似的事情:
function Set-DefaultCredential
{
param
(
[PSCredential]
$Credential
)
$ModuleName = (Get-Item -Path $PSScriptRoot).Parent.Name
$Module = Get-Module -Name $ModuleName
$Commands = $Module.ExportedCommands.GetEnumerator() | Select-Object -ExpandProperty value | Select-Object -ExpandProperty name
foreach ($Command in $Commands)
{
$Global:PSDefaultParameterValues["$Command`:Credential"] = $Credential
}
}
此代码使用$ PSDefaultParameterValues自动变量将您传入的凭据设置为我模块的任何导出命令的默认值。当然,你的逻辑可能不一样,但它可能会向你展示这种方法。
答案 1 :(得分:1)
我有类似的想法,但从来没有必要构建一个完整而干净的实现。存储似乎适合我的连接的状态的一种方法是将该状态封装在PSDrive中。这些驱动器已集成到会话状态中。
这方面的文档并不总是很明显,但它涉及编写一个派生自 $shape->getActiveParagraph()->getBulletStyle()->setBulletType(Bullet::TYPE_BULLET);
$shape->createTextRun('A class library');
$shape->createParagraph()->createTextRun('Written in PHP');
$shape->createParagraph()->createTextRun('Representing a presentation');
$shape->createParagraph()->createTextRun('Supports writing to different file formats');
的类,它具有对凭据管理的内置支持。我记得它,DriveCmdletProvider
方法可以返回一个派生自NewDrive
的自定义类,其中包含私有或内部成员,以存储您需要的内容。
然后,您可以使用PSDriveInfo
和New-PSDrive
建立/断开与驱动器名称的连接连接。 Remove-PSDrive
提供的动态参数允许您根据具体情况自定义参数DriveCmdletProvider
。
有关详细信息,请参阅MSDN here。
答案 2 :(得分:1)
我想最简单的方法是在ps中创建一个高级函数并使用$ script:mycred
但是如果你需要坚持使用纯c#并支持多个运行空间,那么就可以创建一个运行空间ID和标记的字典
public static class TokenCollection {
public static readonly Dictionary<Guid,PSObject> Tokens = new Dictionary<Guid,PSObject>();
}
然后使用令牌
添加当前的运行空间guidGuid runspId = Guid.Empty;
using (var runsp = PowerShell.Create(RunspaceMode.CurrentRunspace))
{
runspId = runsp.Runspace.InstanceId;
TokenCollection.Add(runspId, token);
}
获取此类令牌
PSObject token = null;
if (TokenCollection.Tokens.ContainsKey(runspaceId))
{
token = TokenCollection.Tokens[runspId];
}