合并不同类型的 .NET 4.0 任务/延续



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

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

内部服务 API 是使用任务实现的。使用.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,看起来我在等待continuation()任务完成时显式阻止,而不是指示任务 API 在任务完成后自动继续执行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();
}

你的问题是,如果你不使用ResultContinueWith()会返回Task<Task<HttpResponseMessage>>而不是你需要的Task<HttpResponseMessage>

幸运的是,已经有一种方法可以将任何Task<Task<T>>转换为Task<T>Unwrap() 。因此,只需从ContinueWith() lambda 中return continuation();,然后根据结果调用Unwrap()

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

问题:这是在 .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 上运行的async/await代码。目标包具有适用于 .NET 4.0 的这些新类型。

而不是continuation((。结果使用延续((。等待((

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

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

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

下面似乎相关的问题,答案新的 C# 5.0"异步"和"等待"关键字是否使用多个内核?