简体   繁体   English

尝试使用 C# 中编写的 Cmdlet 获取 PowerShell 中 IAsyncEnumerable 方法的结果

[英]Trying to Get Results of an IAsyncEnumerable Method in PowerShell with a Cmdlet Written in C#

So I am trying to take an IAsyncEnumerable method and relay the results of said method in PowerShell. I understand that PowerShell does not have async support, and generally, people use the Task.GetAwaiter().GetResult() as the means to get their result.所以我试图采用IAsyncEnumerable方法并在 PowerShell 中传递所述方法的结果。我知道 PowerShell 没有异步支持,通常人们使用Task.GetAwaiter().GetResult()作为获取他们的方法结果。

However, this approach does not work (or at least I don't know how to implement it) for IAsyncEnumerable methods.但是,这种方法对IAsyncEnumerable方法不起作用(或者至少我不知道如何实现它)。

My specific case is a little more sophisticated, but let's take this example:我的具体案例有点复杂,但让我们举个例子:

namespace ServerMetadataCache.Client.Powershell
{
    [Cmdlet(VerbsDiagnostic.Test,"SampleCmdlet")]
    [OutputType(typeof(FavoriteStuff))]
    public class TestSampleCmdletCommand : PSCmdlet
    {
        [Parameter(
            Mandatory = true,
            Position = 0,
            ValueFromPipeline = true,
            ValueFromPipelineByPropertyName = true)]
        public int FavoriteNumber { get; set; } = default!;

        [Parameter(
            Position = 1,
            ValueFromPipelineByPropertyName = true)]
        [ValidateSet("Cat", "Dog", "Horse")]
        public string FavoritePet { get; set; } = "Dog";


        private IAsyncEnumerable<int> InternalMethodAsync()
        {
            for (int i = 1; i <= 10; i++)
            {
                await Task.Delay(1000);//Simulate waiting for data to come through. 
                yield return i;
            } 
        }

        protected override void EndProcessing()
        {   
          //this is the issue... how can I tell powershell to read/process results of 
          //InternalMethodAsync()? Regularly trying to read the method doesn't work
          //and neither does wrapping the method with a task and using GetAwaiter().GetResult()
        }

    public class FavoriteStuff
    {
        public int FavoriteNumber { get; set; } = default!;
        public string FavoritePet { get; set; } = default!;
    }

}

This cmdlet is of course a dummy that just takes in a integer and either "Dog", "Cat" or "Horse", but my bigger concern is how to process the InternalMethodAsync() in the Cmdlet.这个 cmdlet 当然是一个虚拟的,只接受 integer 和“狗”、“猫”或“马”,但我更关心的是如何处理 Cmdlet 中的InternalMethodAsync() The challenge is getting around the IAsyncEnumerable .挑战在于绕过IAsyncEnumerable

Make an async wrapper method that takes a concurrent collection - like a ConcurrentQueue<int> - as a parameter and fills it with the items from the IAsyncEnumerable<int> , then start reading from it before the task completes:创建一个异步包装方法,将并发集合(如ConcurrentQueue<int> )作为参数,并用IAsyncEnumerable<int>中的项目填充它,然后在任务完成之前开始读取它:

private async Task InternalMethodExchangeAsync(IProducerConsumerCollection<int> outputCollection)
{
    await foreach(var item in InternalMethodAsync())
        outputCollection.TryAdd(item)
}

Then in EndProcessing() :然后在EndProcessing()中:

protected override void EndProcessing()
{
    var resultQueue = new ConcurrentQueue<int>();
    var task = InternalMethodExchangeAsync(resultQueue);

    // Task still running? Let's try reading from the queue!
    while(!task.IsCompleted)
        if(resultQueue.TryDequeue(out int preliminaryResult))
            WriteObject(preliminaryResult);

    // Process remaining items
    while(resultQueue.Count > 0)
        if(resultQueue.TryDequeue(out int trailingResult))
            WriteObject(trailingResult);
}

This question really helped me figure it out. 这个问题真的帮我弄明白了。

It Goes back to get Task.GetAwaiter().GetResult() - which works on regular tasks.它返回获取 Task.GetAwaiter().GetResult() - 它适用于常规任务。 You need to process the enumerator's results as tasks (which I was trying to do earlier but not in the right way).您需要将枚举器的结果作为任务处理(我之前曾尝试这样做,但方式不正确)。 So based on the InternalMethodAsync() in my question, one could process the results like so:因此,基于我问题中的InternalMethodAsync() ,可以像这样处理结果:

private void processDummy(){
       var enumerator = InternalMethodAsync().GetAsyncEnumerator();
       while (enumerator.MoveNextAsync().AsTask().GetAwaiter().GetResult()){
              Console.WriteLine(enumerator.Current);
       }
}

The AsTask() was the key piece here. AsTask()是这里的关键部分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM