异步函数返回异步元数据(包括结果),而不仅仅是结果。

5
我正在使用.NET Core 3.1编写一个API。该API具有一个异步函数,名为GetSomeProperty(),我在一个端点(称为Get)中使用它。
当从此端点接收响应时,results属性向下“移动”一层,并用异步方法的元数据包装,如下所示:
"results": [
    {
        "result": {//actual result here}
        "id": 1,
        "status": 5,
        "isCanceled": false,
        "isCompleted": true,
        "creationOptions": 0,
        "isFaulted": false
    },
    {
        "result": {//actual result here}
        "id": 2,
        "status": 5,
        "isCanceled": false,
        "isCompleted": true,
        "creationOptions": 0,
        "isFaulted": false
    }
]

我不希望这些结果被包裹在这个“异步”包装器中。

保持方法异步的同时,我如何返回任务结果,而不是包含任务结果的对象?

我没有使用 .Result 的两个原因:

  1. 使用 .Result 被认为是不良做法,因为如果任务尚未完成,它可能会导致锁定。
  2. 我找不到放置它的位置。就我所知,它似乎没有适合的位置。

这是代码(请记住,这已经被大大简化和简化,仅供示例):

[HttpGet]
public async Task<object> Get(string someParameter)
{   
    //Do stuff

    var things = BuildACollectionOfItems();
    var results = things.Select(x => x.IrrelevantThing).OrderBy(x => x.SomethingIrrelevant).Select(async x =>
    {
        return new
        {
            x.Id,
            SomeProperty = await GetSomeProperty(x.Id)
        };
    }).ToArray();

    return new
    {
        Results = ((IEnumerable<object>) results),
        SomeIrrelevantThing = someIrrelevantThing
    };
}

private async Task<bool> GetSomeProperty(int id)
{
    var somethingFromAServer = (await _thingProvider.GetThing()).TheProperty;

    //Do stuff here

    var thing = _context.Stuff.FirstOrDefault(x => x.Thing == somethingFromAServer);

    //Do some more stuff

    return thing.Stuff;
}

2
一则注释:考虑在async方法中使用后缀Asyncasync Task<bool> GetSomePropertyAsync(int id) - dymanoid
2
部分猜测,但是things.Select()是可等待的吗?例如:var results = await things.Select(...) 结构上看起来Get()本身并没有等待任何东西,而结果正在获取任务列表。 - David
你的某处代码中可能缺少了await,导致任务无法执行。建议检查一下那些被强制转换为object类型的变量,因为它们可以引用任何东西,包括Task<object>类型的对象。同时,也要检查一下你的var变量所推断出的实际类型。 - Damien_The_Unbeliever
1
@Jessica:在这种情况下,.Select()IEnumerable上的标准LINQ方法吗?如果是的话,那么这看起来非常有前途:https://dev59.com/qmUp5IYBdhLWcg3w669C 也许需要额外的一些步骤,但是想法是在返回整个结果之前等待所有任务完成。我现在没有测试它的方法,但是只需将该方法末尾的赋值更改为Results = Task.WhenAll(results)可能就可以了。 - David
@David,你真是个明星,这个可行,谢谢 :) - Jessica
显示剩余4条评论
1个回答

5

您的Select返回IEnumerable<Task>(因为它传递了一个async函数),您可以使用Task.WhenAll等待它们全部完成并解开结果:

[HttpGet]
public async Task<object> Get(string someParameter)
{   
    //Do stuff

    var things = BuildACollectionOfItems();
    var results = await Task.WhenAll(things
        .Select(x => x.IrrelevantThing)
        .OrderBy(x => x.SomethingIrrelevant)
        .Select(async x => new
        {
            x.Id,
            SomeProperty = await GetSomeProperty(x.Id)
        }));

    return new
    {
        Results = ((IEnumerable<object>) results),
        SomeIrrelevantThing = someIrrelevantThing
    };
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接