C# 在调用Powershell cmdlet之间保持状态的最佳做法是什么?

C# 在调用Powershell cmdlet之间保持状态的最佳做法是什么?,c#,powershell,cmdlets,C#,Powershell,Cmdlets,我是Powershell开发的新手,我正在尝试用C编写一个Powershell Cmdlet,该Cmdlet将用作REST API的接口 我希望有某种setup Cmdlet,其中会提示用户向restapi输入Uri、用户名和密码,然后像Get Item一样调用Cmldlets,而无需输入这些参数。与用于Powershell的Azure provider非常相似,您可以通过调用Select AzureSubscription设置当前订阅,然后调用Save AzureVhd,而无需再次输入订阅名称

我是Powershell开发的新手,我正在尝试用C编写一个Powershell Cmdlet,该Cmdlet将用作REST API的接口

我希望有某种setup Cmdlet,其中会提示用户向restapi输入Uri、用户名和密码,然后像Get Item一样调用Cmldlets,而无需输入这些参数。与用于Powershell的Azure provider非常相似,您可以通过调用Select AzureSubscription设置当前订阅,然后调用Save AzureVhd,而无需再次输入订阅名称

在对不同cmdlet的调用之间保持状态的最佳实践是什么

编辑: 我不确定这是否是解决这个问题的最佳方法,但我所做的是添加了一个保存状态的singleton类。
我有一个Cmdlet,选择Project-Name MyProject在我的singleton类中设置公共属性,然后我的其他Cmdlet可以访问该属性。

如果它们运行的是V3或更高版本,您可以让安装程序在$PSDefaultParameterValues中设置这些值

见:


有关设置值的详细信息。

可能类似于powershell的CimSession支持?使用new cimsession创建包含状态的会话,然后将cimsession对象传递给其他各种cmdlet。但这不适用于OP中提到的get项

然而,OP中的编辑描述了一个实现,如果我理解正确,它不太可能与get项一起工作

如果get-item支持真的是一个需求,那么我相信get-PSProvider中的PS提供商将是一个不错的选择。PS提供程序将使用诸如get-item之类的cmdlet,并可以通过PSDrives保存状态,就像在get-PSDrive中一样。

您正在寻找PSCmdlet.SessionState

我通过创建一个小桥类MyCmdlet解决了同样的问题,MyCmdlet是我自己所有Cmdlet的派生源,它包含管理会话状态的帮助程序,以及保存要持久化的内容的对象的定义。在本例中,我将只编写一些简单的内容,例如用户名和数据库名称

// In MyCmdlet.cs

public class MyStateInfo {
  public string Username { get; set;}
  public string DbName { get; set;}
}

protected abstract class MyCmdlet : PSCmdlet {
    private const string StateName = "_Blah";

    protected MyStateInfo getState()
    {
        if ( ! SessionState.PSVariable.GetValue(StateName, null) is MyStateInfo s))
            SessionState.PSVariable.Set(StateName, s = new MyStateInfo());

        return s;
    }
}
此时,所有cmdlet都应该从MyCmdlet继承,getState将始终返回一个现有的或新的变量:对类的更改将保留在同一会话中

可能有很多方法可以将其集成到整个cmdlet参数设计中,这对我来说还是一种新方法,因此我不确定这是否是最佳做法,但我通过创建一个helper cmdlet来设置初始值,从而解决了这一问题:

[Cmdlet(VerbsCommon.Set, "MyUsername")]
public class Set_MyUsername : MyCmdlet {
  [Parameter(Mandatory = true, Position = 1)] 
  public string Username {get; set; }

  protected override void ProcessRecord()
  {
       base.ProcessRecord();

       WriteVerbose($"Saving {Username} in session state");
       getState().Username = Username;
  }
}
然后,稍后的某个cmdlet需要执行以下操作:

[Cmdlet(VerbsCommunication.Connect, "Database")]
public class Connect_Database : MyCmdlet {
    [Parameter(Mandatory = false)]
    public string Username { get; set; }

    // other parameters here

    protected override void BeginProcessing()
    {
        base.BeginProcessing();

        if (Username == null)
            Username = getState().Username;

        if (Username == null)
        { // ERROR HERE: missing username }
    }
    // more stuff
}
然后,您的Connect数据库将获取一个显式的Username-steve参数,该参数不咨询或接触会话状态,但如果没有该参数,它将从会话状态对象中提取该参数。如果在此之后仍然缺少用户名,则可能会通过TerminationError失败

您选择的项目可以以相同的方式工作


我通常提供一个Get-MyState cmdlet,它只将状态对象写入管道输出,主要用于调试。如果您的应用程序保证将这些内容分开,那么您不必仅限于一个变量。

也许这是一个解决方案,但当您需要从C代码更新它时,它看起来相当复杂。
[Cmdlet(VerbsCommunication.Connect, "Database")]
public class Connect_Database : MyCmdlet {
    [Parameter(Mandatory = false)]
    public string Username { get; set; }

    // other parameters here

    protected override void BeginProcessing()
    {
        base.BeginProcessing();

        if (Username == null)
            Username = getState().Username;

        if (Username == null)
        { // ERROR HERE: missing username }
    }
    // more stuff
}