4

I have an ASP.Net application (actually Lightswitch is involved here, not sure that it matters much in this case).

I have the following code. It starts in the LogIn.aspx which calls RegisterUser on the OnLoggedIn event.

    public static void RegisterUser(string userName)
    {
        using (var connection = new SqlConnection(IzendaSettings.ConnectionString))
        using (var command = connection.CreateCommand())
        {
            connection.Open();
            command.CommandText = @"SELECT CU.DisplayName, CU.ClientId, C.Name FROM ClientUser CU INNER JOIN Client C on C.ClientId = CU.ClientId WHERE CU.UserName = @userName AND Rights = 0";
            command.Parameters.Add("userName", SqlDbType.NVarChar).Value = userName; 
            using (var reader = command.ExecuteReader())
            {
                if (reader.Read())
                {
                    var unused = RegisterUserAsync(userName, reader.GetString(0), reader.GetInt32(1).ToString(), reader.GetString(0));
                }
            }
        }
    }

    internal static async Task RegisterUserAsync(string userName, string fullName, string clientId, string clientName)
    {
        try
        {
            var token = TokenAuthorization.GetSystemAdministratorToken();
            while (Bootstrap.Initalizing)
            {
                Thread.Yield();
            }
            var izenda = await TenantProcess.Initialize(clientId, clientName, token);
            var connection = await izenda.SetUpConnection(IzendaSettings.ConnectionString);
            await izenda.PutUserInRole("User", userName, fullName, "~/BI/tenantRolePermission.json", connection);
        }
        catch (Exception e)
        {
            Logger.Error("Register User", e);
        }
    }

The async code deadlocks. I'm trying to just run it off in a separate thread, I don't await it on purpose.

Changing the call to RegisterUserAsync to this (with the appropriate capture of the SQL prior to launching the thread):

Task.Run(() => RegisterUserAsync(userName, user.FullName, user.ClientId, user.ClientName));

Solves the problem. I understand why calling Task.Run solves an async call that you want to wait for without deadlocking, but I don't understand why in this case the deadlock was happening. (The Initializing variable was false - I logged a debug statement to make sure, so it never actually did the Thread.Yield, but I wouldn't see why that would affect it anyway).

Why was the deadlock happening in the old code?

Yishai
  • 90,445
  • 31
  • 189
  • 263
  • 4
    `async`/`await` doesn't mean it's on another thread by the way – DavidG Jul 26 '17 at 14:56
  • And if you don't `await` it, it will potentially just not be called at all as the returned `Task` will get GC'd – DavidG Jul 26 '17 at 14:58
  • @DavidG Interesting, I never heard that before (about the GC). – Yishai Jul 26 '17 at 15:01
  • Possible duplicate of [Calling Asynchronous API in ASP.Net Application](https://stackoverflow.com/questions/44828701/calling-asynchronous-api-in-asp-net-application) – Fran Jul 26 '17 at 15:16
  • @Fran, I don't see how that is a duplicate. There the person is attempting to await the call, here I am not. I could see the underlying reason being the same (if I knew the why). – Yishai Jul 26 '17 at 15:26
  • The deadlocking within an asp.net application. See the explanation at the botton of the possible duplicate. Anytime you make an async awaitable call from asp.net you need to .ConfigureAwait(false) because the async call will capture the ASP.net context and deadlock. – Fran Jul 26 '17 at 15:28
  • @Fran, the topic is the same (and the underlying cause is the same, I guess) but one is asking how to await a call in ASP.NET without deadlocking, and this one is asking why it takes explicitly starting a new thread to let it continue in a separate thread without deadlocking (https://meta.stackoverflow.com/questions/254697/when-can-a-question-be-closed-as-a-duplicate-of-another?rq=1) – Yishai Jul 26 '17 at 16:28
  • because the await thread is bound to the ASP.NET request context. if you spin out an entirely new thread then you aren't bound by the ASP.net context, you are bound to the thread pool context – Fran Jul 26 '17 at 18:15

0 Answers0