Why is calling .Result or .Wait() on a Task dangerous?
Quick Answer
Blocking on an async operation with .Result or .Wait() ties up a thread doing nothing, and in a context with a SynchronizationContext (UI, classic ASP.NET) it deadlocks — the continuation needs the thread you're blocking. It also wraps exceptions in AggregateException. Use await all the way.
Two problems. First, in a context with a synchronization context — a desktop app or old ASP.NET — it deadlocks: the async method wants to resume on the thread you've blocked with .Result, and it can't, so both are stuck forever. Second, even in ASP.NET Core where there's no sync context and it doesn't deadlock, you've taken a thread-pool thread and made it sit idle waiting, and enough of those under load starves the pool. Plus exceptions come back wrapped in AggregateException. The answer is always async all the way down.
- Deadlock scenario: the async method's continuation is posted back to the captured context, which is the thread now blocked in
.Result. - ASP.NET Core has no
SynchronizationContextso it won't deadlock — but it still burns a thread-pool thread, contributing to starvation under load. - If you truly must block (a
Mainbefore C# 7.1, a constructor),GetAwaiter().GetResult()at least unwraps the exception. - The real fix is making the whole call chain async.