【问题标题】:Create PowerShell Cmdlets in C# - Pipeline chaining在 C# 中创建 PowerShell Cmdlet - 管道链接
【发布时间】:2020-02-27 19:33:24
【问题描述】:

我在 C# 中有一些类,我想在管道中使用它们,我看过有关它的文章,但我还没有做到。

这是我现在使用它的方式:

$suite = [MyProject.SuiteBuilder]::CreateSuite('my house')

$houseSet = $suite.AddSet('doors', 'These represents doors')
$houseSet.AddOption('blue', 'kitchen')
$houseSet.AddOption('black', 'bedreoom')
$houseSet.AddOption('white', 'toilet')

我希望能够像这样将它与管道一起使用:

$suite = [MyProject.SuiteBuilder]::CreateSuite('my house')

$suite | AddSet('doors', 'These represents doors') `
       | AddOption('blue', 'kitchen') `
       | AddOption('black', 'bedreoom') `
       | AddOption('white', 'toilet')

这是我的 C# 类:

//SuiteBuilder.cs
public static class SuiteBuilder
{
    public static Suite CreateTestSuite(string name)
    {
        return new Suite(name);
    }
}

//Suite.cs
public class Suite : PSCmdlet
{
    public string Name { get; set; }
    public IEnumerable<Set> Sets { get; set; }

    public Suite(string name)
    {
        Name = name;
        Sets = new List<Set>();
    }

    // call this method in pipeline
    public Set AddSet(string type, string description)
    {
        Sets.Add(new Set(type, description));
        return Sets.Last();
    }
}


//Set.cs
public class Set : PSCmdlet
{
    public string Type { get; set; }
    public string Description { get; set; }
    public IEnumerable<Option> Options { get; set; }

    public Set(string type, string description)
    {
        Type = type;
        Description = description;
        Options = new List<Option>();
    }

    // call this method from pipeline
    public Set AddOption(string color, string place)
    {
        Options.Add(new Option(color, place));
        return this;
    }
}


//option.cs
public class Option : PSCmdlet
{
    public string Color { get; set; }
    public string Place { get; set; }

    public Option(string color, string place)
    {
        Color = color;
        Place = place;
    }
}

我正在努力使这些函数可以在管道表单中调用。

我还在我需要调用的每条评论之前添加了一条评论,例如call this method in pipeline

【问题讨论】:

  • 为什么是管道? (对不起,我不能在 cmets 中添加换行符,请继续想象它们)$houseSet = $suite.AddSet('doors', 'These represents doors').AddOption('blue', 'kitchen').AddOption('black', 'bedreoom').AddOption('white', 'toilet') 不够吗?这是一个不复杂的折衷方案,应该可以在您的代码上运行,甚至无需对其进行更改。
  • @ensisNoctis 是的,只是管道看起来更清晰
  • 它说here 你需要使用 System.Management.Automation.ParameterAttribute 来装饰参数,比如[Parameter(ValueFromPipeline = true)]

标签: c# powershell cmdlet


【解决方案1】:

简而言之,您需要:

  • 使用[Parameter(ValueFromPipeline =true)]从管道接受参数
  • 通过在处理方法中调用WriteObject方法向管道提供输出

详细的分步回答

在这篇文章中,我将对您的代码进行一些重构,并向您展示如何在 C# 中创建 Powershell Cmdlet 以及如何定义参数接受来自管道的参数向管道提供输出。然后你可以很容易地写出这样的东西:

$suite = [MyCmdLets.Suite]::New("suite1")
$suite | Add-Set "type1" "desc1"`
       | Add-Option "color1" "place1"`
       | Add-Option "color2" "place2" | Out-Null

为此,请按以下步骤操作:

  1. 创建一个 C# 类库项目(例如将其命名为 MyCmdlets
  2. 安装包Microsoft.PowerShell.5.ReferenceAssemblies
  3. 独立于 PowerShell 创建模型类。 (见帖子底部的代码)
  4. 根据以下注意事项创建 cmdlet:(请参阅帖子底部的代码)

    • 为每个 cmdlet 创建一个 C# 类
    • 派生自Cmdlet
    • 使用CmdletAttribute 属性来装饰类,指定动词和动词后的名称,例如,如果你想拥有Add-Set,请使用[Cmdlet(VerbsCommon.Add, "Set")]
    • 如果你想有一个管道的输出,用OutputTypeAttribute属性来装饰类,指定输出的类型,例如,如果你想有一个类型为Set的输出管道,使用[OutputType(typeof(Set))]
    • 为 cmdlet 的每个输入参数定义一个 C# 属性。
    • Parameter 属性装饰每个参数属性。
    • 如果你想接受来自管道的参数,当使用ParameterAttribute属性进行装饰时,将ValueFromPipeline设置为true,例如[Parameter(ValueFromPipeline =true)
    • 要向管道提供输出,请覆盖 ProcessRecord 等管道处理方法并使用 WriteObject 将其写入输出。
  5. 构建项目。

  6. 打开 PowerShell ISE 并运行以下代码:

    Import-Module "PATH TO YOUR BIN DEBUG FOLDER\MyCmdlets.dll"
    
    $suite = [MyCmdLets.Suite]::New("suite1")
    $suite | Add-Set "type1" "desc1"`
           | Add-Option "color1" "place1"`
           | Add-Option "color2" "place2" | Out-Null
    

    它将创建一个这样的结构:

    Name   Sets           
    ----   ----           
    suite1 {MyCmdlets.Set}
    
    
    Type  Description Options                             
    ----  ----------- -------                             
    type1 desc1       {MyCmdlets.Option, MyCmdlets.Option}
    
    
    Color  Place 
    -----  ----- 
    color1 place1
    color2 place2
    

示例代码

模型类

如上所述,像这样设计独立于 PowerShell 的模型类:

using System.Collections.Generic;
namespace MyCmdlets
{
    public class Suite
    {
        public string Name { get; set; }
        public List<Set> Sets { get; } = new List<Set>();
        public Suite(string name) {
            Name = name;
        }
    }
    public class Set
    {
        public string Type { get; set; }
        public string Description { get; set; }
        public List<Option> Options { get; } = new List<Option>();
        public Set(string type, string description) {
            Type = type;
            Description = description;
        }
    }
    public class Option 
    {
        public string Color { get; set; }
        public string Place { get; set; }
        public Option(string color, string place) {
            Color = color;
            Place = place;
        }
    }
}

CmdLet 类

还可以根据我上面描述的注释设计 cmdlet 类:

using System.Management.Automation;
namespace MyCmdlets
{
    [Cmdlet(VerbsCommon.Add, "Set"), OutputType(typeof(Set))]
    public class AddSetCmdlet : Cmdlet
    {
        [Parameter(ValueFromPipeline = true, Mandatory = true)]
        public Suite Suite { get; set; }
        [Parameter(Position = 0, Mandatory = true)]
        public string Type { get; set; }
        [Parameter(Position = 1, Mandatory = true)]
        public string Description { get; set; }
        protected override void ProcessRecord() {
            var set = new Set(Type, Description);
            Suite.Sets.Add(set);
            WriteObject(set);
        }
    }

    [Cmdlet(VerbsCommon.Add, "Option"), OutputType(typeof(Option))]
    public class AddOptionCmdlet : Cmdlet
    {
        [Parameter(ValueFromPipeline = true, Mandatory = true)]
        public Set Set { get; set; }
        [Parameter(Position = 0, Mandatory = true)]
        public string Color { get; set; }
        [Parameter(Position = 1, Mandatory = true)]
        public string Place { get; set; }
        protected override void ProcessRecord() {
            var option = new Option(Color, Place);
            Set.Options.Add(option);
            WriteObject(Set);
        }
    }
}

【讨论】:

    【解决方案2】:

    您可以使用 ValueFromPipeline = $true。但是,如果要继续流水线,则必须引用类型变量并返回项目。我不知道解决这个问题的方法。由于它会返回,因此您必须在末尾添加一个 Out-Null 以防止它访问控制台。

    https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_ref?view=powershell-6

    function Add-Option {
        param(
            [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
            [ref]$Item,
            [Parameter(Mandatory = $true, Position = 0)]
            [String]$Color
            [Parameter(Mandatory = $true, Position = 1)]
            [String]$Room
        )
        $Item.Value.AddOption($Color,$Room)
        return $Item
    }
    
    $suite = [MyProject.SuiteBuilder]::CreateSuite('my house')
    
    [ref]$suite | Add-Option 'blue' 'kitchen' `
                | Add-Option 'black' 'bedroom' `
                | Out-Null
    
    

    【讨论】:

    • 注意:我将 AddOption 更改为 Add-Option 以获得正确的架构,但您可以根据需要使用 AddOption。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多