1

所以我试图采用一种IAsyncEnumerable方法并在 PowerShell 中传递所述方法的结果。我知道 PowerShell 没有异步支持,通常,人们使用它Task.GetAwaiter().GetResult()作为获得结果的手段。

但是,这种方法对方法不起作用(或者至少我不知道如何实现它)IAsyncEnumerable

我的具体情况要复杂一些,但让我们举个例子:

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!;
    }

}

这个 cmdlet 当然是一个虚拟的,只接受一个整数和“狗”、“猫”或“马”,但我更关心的是如何InternalMethodAsync()在 Cmdlet 中处理。挑战在于绕过IAsyncEnumerable.

4

2 回答 2

1

创建一个异步包装器方法,该方法将并发集合(如 a ConcurrentQueue<int>-)作为参数并用 中的项目填充它IAsyncEnumerable<int>,然后在任务完成之前开始读取它:

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

然后在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);
}
于 2021-10-06T20:40:19.450 回答
0

这个问题真的帮助我弄清楚了。

它返回获取 Task.GetAwaiter().GetResult() - 它适用于常规任务。您需要将枚举器的结果作为任务处理(我之前尝试过但不是以正确的方式)。因此,根据InternalMethodAsync()我的问题,可以像这样处理结果:

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

AsTask()是这里的关键部分。

于 2021-10-07T03:34:48.330 回答