Eu tenho o seguinte código WebAPI de teste, não uso WebAPI na produção, mas fiz isso devido a uma discussão que tive sobre esta questão: Pergunta WebAPI Async
De qualquer forma, aqui está o método WebAPI ofensivo:
public async Task<string> Get(int id)
{
var x = HttpContext.Current;
if (x == null)
{
// not thrown
throw new ArgumentException("HttpContext.Current is null");
}
await Task.Run(() => { Task.Delay(500); id = 3; });
x = HttpContext.Current;
if (x == null)
{
// thrown
throw new ArgumentException("HttpContext.Current is null");
}
return "value";
}
Eu acreditava aqui que a segunda exceção é esperada porque, quando o for await
concluído, provavelmente será em um segmento diferente, onde HttpContext.Current
uma variável estática do segmento não resolverá mais para o valor apropriado. Agora, com base no contexto de sincronização, ele poderia realmente ser forçado a voltar para o mesmo encadeamento após o await, mas não estou fazendo nada sofisticado em meu teste. Este é apenas um uso simples e ingênuo de await
.
Em comentários em outra questão foi-me dito que HttpContext.Current
deveria resolver depois de esperar. Há até outro comentário sobre esta questão indicando o mesmo. Então, o que é verdade? Deve resolver? Eu acho que não, mas eu quero uma resposta confiável sobre isso porque async
e await
é novo o suficiente para não conseguir encontrar nada definitivo.
TL; DR: É HttpContext.Current
potencialmente null
após um await
?
fonte
AspNetSynchronizationContext
isso que cuidaHttpContext
, nãoawait
. Além disso, o retorno de chamada de continuação paraawait
pode (e provavelmente irá) ocorrer em um thread diferente para o modelo de execução de API da Web..ConfigureAwait(false)
algum ponto da linha. Não há solicitação ou controlador explicitamente transmitido pela camada de negócios, uma vez que esta não tem conhecimento da web. Isso é útil, por exemplo, para um módulo de registro que pode injetar os detalhes da solicitação quando a lógica de negócios grava um genéricoTraceInformation
.Respostas:
Certifique-se de estar escrevendo um aplicativo ASP.NET 4.5 e visando 4.5.
async
eawait
têm comportamento indefinido no ASP.NET, a menos que você esteja executando no 4.5 e esteja usando o novo contexto de sincronização "amigável para tarefas".Em particular, isso significa que você deve:
httpRuntime.targetFramework
como4.5
, ouappSettings
, definaaspnet:UseTaskFriendlySynchronizationContext
comotrue
.Mais informações estão disponíveis aqui .
fonte
<httpRuntime targetFramework="4.5" />
é o que resolveu, obrigado por esclarecer.targetFramework
como 4.5.1 ou 4.5, mas o assíncrono em 4.5.1 deve funcionar bem.Como @StephenCleary corretamente apontou, você precisa disso em seu web.config:
<httpRuntime targetFramework="4.5" />
Quando eu estava solucionando isso pela primeira vez, fiz uma pesquisa ampla de solução para o acima, confirmei que estava presente em todos os meus projetos da web e rapidamente descartei-o como o culpado. Por fim, ocorreu-me olhar para os resultados da pesquisa em todo o contexto:
<!-- For a description of web.config changes for .NET 4.5 see http://go.microsoft.com/fwlink/?LinkId=235367. The following attributes can be set on the <httpRuntime> tag. <system.Web> <httpRuntime targetFramework="4.5" /> </system.Web> -->
Doh.
Lição: Se você atualizar um projeto da web para 4.5, ainda precisará fazer essa configuração manualmente.
fonte
Seu teste não é falho e HttpContext.Current não deve ser nulo após o await porque na API Web ASP.NET quando você espera, isso garantirá que o código que segue esse await seja transmitido ao HttpContext correto que estava presente antes do await.
fonte
Eu encontrei esse problema recentemente. Como Stephen apontou, não definir explicitamente a estrutura de destino pode gerar esse problema.
No meu caso, nossa API da Web foi migrada para a versão 4.6.2, mas a estrutura de destino do tempo de execução nunca foi especificada na configuração da web, então basicamente isso estava faltando na tag <system.web>:
Se você tiver dúvidas sobre a versão do framework que está executando, isso pode ajudar: Adicione a seguinte linha em qualquer um dos seus métodos de API da Web e defina um ponto de interrupção para verificar que tipo está carregado atualmente em tempo de execução e verifique se não é uma implementação Legacy:
Você deve ver isto (AspNetSynchronizationContext):
Em vez de LegazyAspNetSynchronizationContext (que foi o que vi antes de adicionar a estrutura de destino):
Se você acessar o código-fonte ( https://referencesource.microsoft.com/#system.web/LegacyAspNetSynchronizationContext.cs ), verá que a implementação herdada dessa interface carece de suporte assíncrono.
Passei muito tempo tentando encontrar a origem do problema e a resposta de Stephen ajudou muito. Espero que esta resposta forneça mais informações sobre o problema.
fonte