Preciso chamar um async
método em um catch
bloco antes de lançar novamente a exceção (com seu rastreamento de pilha) como este:
try
{
// Do something
}
catch
{
// <- Clean things here with async methods
throw;
}
Mas, infelizmente, você não pode usar await
em um bloco catch
ou finally
. Aprendi que é porque o compilador não tem como voltar em um catch
bloco para executar o que está depois de sua await
instrução ou algo assim ...
Tentei usar Task.Wait()
para substituir await
e obtive um impasse. Pesquisei na Web como poderia evitar isso e encontrei este site .
Como não posso alterar os async
métodos nem sei se eles usam ConfigureAwait(false)
, criei esses métodos que usam um Func<Task>
que inicia um método assíncrono quando estamos em um thread diferente (para evitar um deadlock) e aguarda sua conclusão:
public static void AwaitTaskSync(Func<Task> action)
{
Task.Run(async () => await action().ConfigureAwait(false)).Wait();
}
public static TResult AwaitTaskSync<TResult>(Func<Task<TResult>> action)
{
return Task.Run(async () => await action().ConfigureAwait(false)).Result;
}
public static void AwaitSync(Func<IAsyncAction> action)
{
AwaitTaskSync(() => action().AsTask());
}
public static TResult AwaitSync<TResult>(Func<IAsyncOperation<TResult>> action)
{
return AwaitTaskSync(() => action().AsTask());
}
Portanto, minhas perguntas são: Você acha que este código está correto?
Claro, se você tiver algumas melhorias ou conhecer uma abordagem melhor, estou ouvindo! :)
fonte
await
em um bloco catch é permitido desde C # 6.0 (veja minha resposta abaixo)Respostas:
Você pode mover a lógica para fora do
catch
bloco e relançar a exceção depois, se necessário, usandoExceptionDispatchInfo
.Dessa forma, quando o chamador inspeciona a
StackTrace
propriedade da exceção , ele ainda registra ondeTaskThatFails
ela foi lançada.fonte
ExceptionDispatchInfo
vez deException
(como na resposta de Stephen Cleary)?Exception
, você perderá todo o anteriorStackTrace
?Você deve saber que, desde o C # 6.0, é possível usar blocos
await
incatch
efinally
, portanto, você pode fazer o seguinte:Os novos recursos do C # 6.0, incluindo o que acabei de mencionar, estão listados aqui ou como um vídeo aqui .
fonte
Se você precisar usar
async
gerenciadores de erros, recomendo algo assim:O problema com o bloqueio síncrono no
async
código (independentemente do thread em que esteja sendo executado) é que você está bloqueando de forma síncrona. Na maioria dos cenários, é melhor usarawait
.Atualização: Já que você precisa relançar, você pode usar
ExceptionDispatchInfo
.fonte
throw exception;
naif
instrução, o rastreamento da pilha será perdido.Extraímos grande resposta do hvd para a seguinte classe de utilitário reutilizável em nosso projeto:
Seria usado da seguinte maneira:
Sinta-se à vontade para melhorar a nomenclatura, nós o mantivemos intencionalmente prolixo. Observe que não há necessidade de capturar o contexto dentro do wrapper, pois ele já está capturado no site da chamada, portanto
ConfigureAwait(false)
.fonte