Nikon the Third Nikon the Third - 2 months ago 28
C# Question

Why is async/await not working in my ASP.net 5 Console Application?

I tried this simple ASP.net 5 Console Application on both Windows (.NET 4.5.1) and Linux (Mono 4.0.1), both times with the same result.

Note: I called it an ASP.net 5 Console Application because that is what is was called in Visual Studio up to RC. Now it is called Console Application (Package), but it still uses DNX from https://github.com/aspnet/dnx :)

My

Program.cs
:

using System;
using System.Threading.Tasks;

namespace ConsoleApplication
{
public class Program
{
public async void Main(String[] args)
{
#if DNX451
AppDomain.CurrentDomain.UnhandledException +=
(s, e) => Console.WriteLine(e);
#endif

try
{
await Task.Delay(1000);
Console.WriteLine("After Task.Delay");
}
finally
{
Console.WriteLine("Inside Finally");
}
}
}
}


My
project.json
:

{
"version": "1.0.0-*",
"dependencies": {},
"commands": {
"ConsoleApplication": "ConsoleApplication"
},
"frameworks": {
"dnx451": {}
}
}


When running with either
1.0.0-beta4 CLR
or
1.0.0-beta5-11904 CLR
, the command
dnx . ConsoleApplication
prints nothing. The program exits with status code 0 as soon as it encounters the
Task.Delay
. Even the finally block is never executed.

I couldn't test .NET Core 5.0 because
dnu restore
says everything is OK, but the packages can't be located by the runtime. Oh well...

Does anybody have the same problems with async/await and DNX? Or spot some error I made?

Answer

If you see my question (and answer) in Entry point can be marked with the 'async' modifier on CoreCLR?, you'll see that at the top-most of the call-stack, you have the following:

public static int Execute(string[] args)
{
    // If we're a console host then print exceptions to stderr
    var printExceptionsToStdError = Environment
                                    .GetEnvironmentVariable
                                     (EnvironmentNames.ConsoleHost) == "1";

    try
    {
        return ExecuteAsync(args).GetAwaiter().GetResult();
    }
    catch (Exception ex)
    {
        if (printExceptionsToStdError)
        {
            PrintErrors(ex);
            return 1;
        }

        throw;
    }
}

Internally, it checks to see the return type of the method, if the return type is of type Task, then it registers a ContinueWith, which it'll be able to synchronously wait on:

if (result is Task)
{
    return ((Task)result).ContinueWith(t =>
    {
        return 0;
    });
}

When you pass in async void, it looks to Execute as if this method is a "fire and forget" void returning method. That's why it never finishes execution. But, if you change it to return a Task, it'll work:

public async Task Main(String[] args)
{
    #if DNX451
    AppDomain.CurrentDomain.UnhandledException += 
        (s, e) => Console.WriteLine(e);
    #endif

    try
    {
        await Task.Delay(1000);
        Console.WriteLine("After Task.Delay");
    }
    finally
    {
        Console.WriteLine("Inside Finally");
    }
}