Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 任务ContinueWith未返回预期值_C#_.net_Asp.net Web Api_Task Parallel Library_Async Await - Fatal编程技术网

C# 任务ContinueWith未返回预期值

C# 任务ContinueWith未返回预期值,c#,.net,asp.net-web-api,task-parallel-library,async-await,C#,.net,Asp.net Web Api,Task Parallel Library,Async Await,这可能是RTFM类型的问题之一,但是,我一辈子都搞不清楚如何以一种适用于Asp.net WebApi的方式将任务链接在一起 具体来说,我正在研究如何在控制器处理响应后使用DelegatingHandler修改响应(添加额外的头),并尝试使用HttpMessageInvoker实例对DelegatingHandler进行单元测试 第一模式 此操作在continueTask.Start()上失败,但System.InvalidOperationException除外:不能在继续任务上调用Start。

这可能是RTFM类型的问题之一,但是,我一辈子都搞不清楚如何以一种适用于Asp.net WebApi的方式将任务链接在一起

具体来说,我正在研究如何在控制器处理响应后使用
DelegatingHandler
修改响应(添加额外的头),并尝试使用
HttpMessageInvoker
实例对
DelegatingHandler
进行单元测试

第一模式 此操作在
continueTask.Start()
上失败,但System.InvalidOperationException除外:不能在继续任务上调用Start。

第三种模式 如果我将生产代码更改为返回延续任务,而不是从
base.SendAsync
返回结果,那么我将得到关于调用延续任务的
Start
的第二个单元测试异常

我想我想在我的生产代码中完成第三个单元测试模式,但是,我不知道如何编写它

如何执行我想要的操作(在调用断言之前添加头)?

返回映射的任务,因此您需要返回该任务:

var task = base.SendAsync(request, cancellationToken);
return task.ContinueWith(AddWwwAuthenticateHeaderTask());

返回
任务
时,它应该始终是
热任务
,这意味着返回的任务已经启动。让某人对返回的任务显式调用
Start()
,这会让人感到困惑,并且违反了指导原则

要正确查看延续结果,请执行以下操作:

protected override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
{
    return base.SendAsync(request, cancellationToken).ContinueWith(AddWwwAuthenticateHeaderTask()).Unwrap();
}
受保护的覆盖任务SendAsync(
HttpRequestMessage请求,CancellationToken CancellationToken)
{
返回base.sendaync(请求,取消令牌).ContinueWith(AddWwwAuthenticateHeaderTask()).Unwrap();
}
您应该修改
base.SendAsync
以返回已启动的
任务

从:

从TAP方法返回的所有任务都必须是“热的”。如果TAP方法在内部使用任务的构造函数来实例化要返回的任务,则TAP方法必须在返回任务对象之前对其调用Start。TAP方法的使用者可以安全地假设返回的任务是“热的”,并且不应该尝试调用从TAP方法返回的任何任务的Start。对“热”任务调用Start将导致InvalidOperationException(该检查由task类自动处理)


尝试以下方法。注意
task2.Unwrap()
,我认为其他答案没有提到这一部分:

protected override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
{
    var task1 = base.SendAsync(request, cancellationToken);

    var task2 = task1.ContinueWith(t => AddWwwAuthenticateHeaderTask(),
        cancellationToken);

    return task2.Unwrap();
}
无论哪种情况,都不会在原始同步上下文(可能是
aspnetsynchronizationcontext
)上继续。如果需要保持相同的上下文,请使用
TaskScheduler.FromCurrentSynchronizationContext()
而不是
TaskScheduler.Default
。警告一句:在ASP.NET中

我一辈子都搞不清楚如何以一种适用于Asp.net WebApi的方式将任务链接在一起

接受
async
wait
。特别是,将
ContinueWith
替换为
wait
(不要使用任务构造函数或
Start
):

受保护的覆盖异步任务SendAsync(
HttpRequestMessage请求,CancellationToken CancellationToken)
{
var response=await base.sendaync(请求、取消令牌);
if(response.StatusCode==HttpStatusCode.Unauthorized)
{
response.Headers.WwwAuthenticate.Add(
新的AuthenticationHeaderValue(“基本”、“领域=\“api\”);
}
返回响应;
}

您使用的是哪个.NET framework?即使任务已经完成,也会运行Continuations。你认为这不是导致你过度谨慎的开始行为的原因。没有必要。谢谢你提供的指导意见,看起来我是在违反规则。原来我的问题是我的
DelegatingHttpMessageHandler
类,我没有开始它创建的任务。
public class BasicAuthenticationHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var task = base.SendAsync(request, cancellationToken);

        task.ContinueWith(AddWwwAuthenticateHeaderTask());

        return task;
    }

    private static Func<Task<HttpResponseMessage>, HttpResponseMessage> 
        AddWwwAuthenticateHeaderTask()
    {
        return task =>
        {
            var response = task.Result;

            if (response.StatusCode == HttpStatusCode.Unauthorized)
            {
                response.Headers.WwwAuthenticate.Add(
                    new AuthenticationHeaderValue("Basic", "realm=\"api\""));
            }

            return response;
        };
    }
}
[TestMethod]
public void should_give_WWWAuthenticate_header_if_authentication_is_missing()
{
    using (var sut = new BasicAuthenticationHandler())
    {
        sut.InnerHandler = new DelegatingHttpMessageHandler(
            () => new HttpResponseMessage(HttpStatusCode.Unauthorized));

        using (var invoker = new HttpMessageInvoker(sut))
        {
            var task = invoker.SendAsync(_requestMessage, CancellationToken.None);

            task.Start();

            Assert.IsTrue(
                task.Result.Headers.WwwAuthenticate.Contains(
                    new AuthenticationHeaderValue("Basic", "realm=\"api\"")));
        }
    }
}
var task = base.SendAsync(request, cancellationToken);
return task.ContinueWith(AddWwwAuthenticateHeaderTask());
protected override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
{
    return base.SendAsync(request, cancellationToken).ContinueWith(AddWwwAuthenticateHeaderTask()).Unwrap();
}
protected override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
{
    var task1 = base.SendAsync(request, cancellationToken);

    var task2 = task1.ContinueWith(t => AddWwwAuthenticateHeaderTask(),
        cancellationToken);

    return task2.Unwrap();
}
var task2 = task1.ContinueWith(
    t => AddWwwAuthenticateHeaderTask(), 
    cancellationToken,
    TaskContinuationOptions.ExecuteSynchronously,
    TaskScheduler.Default);
protected override async Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
{
    var response = await base.SendAsync(request, cancellationToken);
    if (response.StatusCode == HttpStatusCode.Unauthorized)
    {
        response.Headers.WwwAuthenticate.Add(
            new AuthenticationHeaderValue("Basic", "realm=\"api\""));
    }

    return response;
}