0

I have a class constructor in the form:

protected LoginFunctions(){…..}

Within this function I call an async function using :

var tsk = Task.Run(async () => await PrePopulateLogin());

my trouble is that the PrePopulateLogin function is still being run when the constructor is exited. How can I change my code so that the constructor doesn't exit until after PrePopulateLogin has finished?

The PrePopulateLogin has to be async.

private async Task PrePopulateLogin()
    {
        var vector = await JSRuntime.Current.InvokeAsync<string>("blazorExtensions.ReadCookie", "vector");
        var secureNextLogon = await JSRuntime.Current.InvokeAsync<string>("blazorExtensions.ReadCookie", "SecureNextLogon");

        if (secureNextLogon != null)
        {
            var encryptor = new AesEncryptor(SecurityConstants.SecurityKey, Convert.FromBase64String(vector));
            var decryptedValue = encryptor.Decrypt(secureNextLogon);
            var email = decryptedValue.Split(' ')[0].Substring(6);
            var password = decryptedValue.Split(' ')[1].Substring(9);
            var rememberMe = decryptedValue.Split(' ')[2].Substring(11);
            if (rememberMe == "True")
            {

                UserModel.EmailAddress = email;
                UserModel.Password = password;
                UserModel.RememberUser = true;
            }
        }
    }
bilpor
  • 2,357
  • 5
  • 21
  • 55
  • Possibly related / duplicate: https://stackoverflow.com/questions/23048285/call-asynchronous-method-in-constructor – StuartLC Jan 07 '19 at 08:54
  • why not put a `.Wait()` or something like `tsk.result;` – vikscool Jan 07 '19 at 08:55
  • 3
    Reconsider making your constructor so that it makes the object _ready to do work_, not to actually do that work. Move that logic to another method, and call it after you've constructed your object - presumably the code where you're calling it is `async`, where you'd be able to properly `await` it? – James Thorpe Jan 07 '19 at 08:56
  • What is forcing that `PrePopulateLogin` "has to be async"? Because it doesn't seem to make a great deal of sense to use `Task.Run` ("I have some work to be done but can't do it on my thread because it's busy doing other things") immediately followed by saying "I've nothing useful to do until *that `Task` over there* has finished running". – Damien_The_Unbeliever Jan 07 '19 at 08:58
  • Don't call any methods in constructor. Constructors should not do any work. – FCin Jan 07 '19 at 09:09
  • @FCin this is a Blazor project (not that that really matters). We have a login form. On initial Login, based on user input we may or may not save these in a Cookie. When the user next logs in, from the constructor I have to look for the cookie and if found based on one of the settings either pre-populate or not the form. The constructor is the best place to do this. For the cookies I have to use JSInterop as Blazor hasn't fully implemented the HTTPContext yet for working with cookies. The interop requires the use of Async methods. – bilpor Jan 07 '19 at 09:14
  • [Some suggestions by Stephen Cleary](https://blog.stephencleary.com/2013/01/async-oop-2-constructors.html) – Matthew Watson Jan 07 '19 at 09:17
  • That stuff should put put into a service class and injected into LoginFunctions. Expose a method that decrypts the cookie and returns the information you need. Call it when you need it, e.g. from the action method (which is async and will let you make these calls properly). Async + constructors don't mix. – John Wu Jan 07 '19 at 09:40
  • @FCin I'd say a better rule is "don't call any _virtual_ methods in a constructor." I disagree with the notion that the constructor should never do any work. Often it is a uniquely perfect opportunity to provide one-time initialization that might bear some cost but which can be done in an entirely threadsafe way (since there can be no contention for your object in its constructor), and moreover where the one-time-cost must be born anyway and it's preferable to bear it early rather than penalizing the first consumer. – Kirk Woll Jan 08 '19 at 07:31

1 Answers1

3

Finally found how to do it with Blazor.

I needed to remove the call from the constructor and instead override the BlazorComponents initializer event thus:

protected override async Task OnInitAsync()
{
    await PrePopulateLogin();
}
bilpor
  • 2,357
  • 5
  • 21
  • 55
  • Yeah, whenever you need async in a constructor it means you need to find a different way. For a Blazor situation like you have here, this approach is correct. Other times, creating static async factory methods can be an option. – Kirk Woll Jan 08 '19 at 07:26