3

我目前正在实现一个System.Web.Http.IActionFilter调用内部服务来确定当前请求是否可以继续的方法。我遇到的问题是Task<T1>基于 a 封装的一段逻辑返回 a Task<T2>

一个例子可能会有所帮助。

内部服务 API 使用 Tasks 实现。使用.NET 4.5的 async/await 逻辑很简单:

public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
    UserAuthenticationResult authResult = await HitInternalServiceAsync();

    if (!authResult.IsAuthenticated)
    {
        throw new HttpResponseException("User is not authenticated", HttpStatusCode.Unauthorized);
    }

    return await continuation();
}

然而,使用.NET 4.0中的旧任务 API 会更加困难;

public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
    return HitInternalServiceAsync()
            .ContinueWith(t1 => {
                UserAuthenticationResult authResult = t1.Result;

                if (!authResult.IsAuthenticated)
                {
                    throw new HttpResponseException("User is not authenticated", HttpStatusCode.Unauthorized);
                }

                //Hack hack - this blocks the thread until the task retuned by continuation() completes
                return continuation().Result;
            });
}

当身份验证检查成功时,困难的部分就来了——然后我想等待延续函数返回的任务。

使用 .NET 4.0 时,我似乎在等待任务完成时明确阻止,而不是指示 Tasks API在continuation()的任务完成后自动继续执行任务。continuation()

问题:这是在 .NET 4.0 中实现此行为的唯一方法吗?

给定一个足够复杂的内部服务 API,我可以很容易地看到等待其他任务的任务数量迅速增加。

编辑:看起来上面的4.0代码也不可行 - 因为延续 lambda 不会在 ASP.NET 线程上下文服务中执行,如 HttpContext.Current 不可用。更好的实现将是...

public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
    Task<UserAuthenticationResult> authResultTask = HitInternalServiceAsync();

    var authResult = authResultTask.Result;

    if (!authResult.IsAuthenticated)
    {
        throw new HttpResponseException("User is not authenticated", HttpStatusCode.Unauthorized);
    }

    return continuation();
}
4

3 回答 3

5

您的问题是,如果您不使用Result,ContinueWith()将返回Task<Task<HttpResponseMessage>>而不是Task<HttpResponseMessage>您需要的。

幸运的是,已经有一种方法可以将 any 转换Task<Task<T>>Task<T>: Unwrap()。所以只是return continuation();ContinueWith()lambda 然后调用Unwrap()结果。

如果您希望继续在 ASP.NET 上下文中执行,您可以使用TaskScheduler.FromCurrentSynchronizationContext().

于 2012-05-03T11:09:22.070 回答
3

问题:这是在 .NET 4.0 中实现此行为的唯一方法吗?

async/await是 C# 5.0 功能,而不是 .NET 4.5 功能。它确实使用了 .NET 4.5 中引入的某些类型,但没有其他原因需要新的运行时。

如果您使用的是 VS2010 (C# 4.0),svick 的答案是最好的。

但是,如果您使用的是 VS11 Beta (C# 5.0),还有另一种选择:您可以使用异步目标包编写在 .NET 4.0 上运行的代码。目标包具有 .NET 4.0 的这些新类型。asyncawait

于 2012-05-03T12:59:07.213 回答
1

而不是 continuation().Result 使用 continuation().Wait()

task.wait 是阻止任务的适当方式。

根据 MSDN 文档,Task.Wait 方法:等待任务完成执行。

http://msdn.microsoft.com/en-us/library/dd235635.aspx

以下似乎相关的问题,答案 是新的 C# 5.0 'async' 和 'await' 关键字是否使用多个内核?

于 2012-05-03T03:20:04.440 回答