0
votes

I'm calling an async method (LoginAsync) for Authorization.

 UserObject = clienAuthenticationService.LoginAsync(sUser).Result;
 if (UserObject != null)
 {
    // Proceed for further code;
 }
 else
 {
    // Show Error message of failed authentication;
 }

As this is third party service, I need output of LoginAsync before I go to next line (If block). Current code is working fine for me and I know it waits until LoginAsync is executed.

However, I'm getting recommendation to Replace this use of 'Task.Result' with 'await'.

Question : I'm not sure if my case is relevant for await, since I need kind of Synchronous execution of async method. Can someone please confirm if recommendation given to me is irrelevant in my case or there is way to call async method synchronously with await ?

EDIT : After comments, I modified my code to add await instead of .Result. Still, unable to read response from async method. Below is new code

public static async Task<bool> CallJimService(LoginUserClass sUser)
{

    var result = clientJIMServiceCall.LoginAsync(sUser).ConfigureAwait(false);
  
    LoginUserClass loginUserDetails = await result;

    if (loginUserDetails != null && loginUserDetails.UserProperties != null && loginUserDetails.UserProperties.LoggedIn)
    {
        return true;
    }
    else
    {
        return false;
    }
  
 }
 
 
 
  public static Boolean ValidateUserCredentails(string pstrUserName, string pstrPassWord)
  {
        LoginUserClass sUser = new LoginUserClass();
        sUser.UserName = pstrUserName;
        sUser.Password = pstrPassWord;
        
        return CallJimService(sUser).Result; // Here I want to avoid using .Result.
  }

Also, I don't want to change ValidateUserCredentails() to async. How Can I get through this?

2
What you are doing is blog.stephencleary.com/2012/07/dont-block-on-async-code.html. Yes, you should replace it with await. You do not want to execute the code synchronously, you want to execute it asynchronously but only continue after it's done. This is exactly what await was invented for. - GSerg
Keep in mind that Async Await, as it should be used here, does not mean multi-threaded (TPL). Async Await was designed to allow the process to give the thread back to the O/S until the I/O operation completes. That way the thread is not blocking/waiting which is a waste of resources especially on web servers where threads can be in high demand. So what you should be doing here is using async/await which suspends the thread at LoginAsync and then continues the execution as soon as a result is returned. - Igor
Its hard to help you further or answer this, because you should not be calling Result or block on an async method period. let async propagate... if you live by that single rule, warnings, weird issues, deadlocks and aggregate exceptions will dissipate. - TheGeneral
@GSerg , and Igor, and 00110001 - With due respect to answers, comments have been more helpful. Still unsure about one thing. I don't want to replace my all calling functions to async. This would be big change for me. I changed caller of LoginAsync method to async and made it return Task<bool>. However, unable to read this response in non-async method. - AK47
@AK47 And yet, making all calling functions async is the way to go. Please see stackoverflow.com/a/14189020/11683 for "why" and for "how not to". - GSerg

2 Answers

2
votes

The LoginAsync method is marked async, and returns a Task. This method is expected to have an asynchronous implementation. You cannot execute this method synchronously, because this method does not have a synchronous implementation. If the class you are using would provide a synchronous implementation of the Login operation, they would have to expose a Login method.

There is no guarantee that calling this method asynchronously will result in an asynchronous operation. Asynchronous methods may return synchronously if the state needs them to. For instance, AsyncLogin may return synchronously if you already have logged in. However, unless doing something extremely specific, there must be at least one execution path in the method that requires some asynchronous operation.

Recommendation given to you is not irrelevant.

You should not block on an async operation, especially using Task.Result. Asynchronous operations does not involve multithreading, and awaiting on a Task.Result may provoke a deadlock of your application, because your thread will be waiting on the Result and will not be able to respond to the async operation signaling completion.

You'd rather await the LoginAsync method asynchronously, making your method async as well.

If you are forced to call the LoginAsync method from a synchronous context, the correct implementation will depend on what exactly you are trying to achieve and what the LoginAsync method does.

1
votes

If you are able to use await (that is if you are in an async method already), then use await.

UserObject = await clienAuthenticationService.LoginAsync(sUser);

Using await helps the code run synchronous(that is it preserves flow of control). I will try to explain async usage through the following example, which has asynchronous execution flow, but synchronous control flow - Following code runs out of sync (asynchronous that is) -

// Authenticate the user, but don't wait here while authenticating.
// Async part could be - Checking database for user details. (Network IO operation)
var UserAuthTask = LoginAsync();

// Prepare a layout or something to show when authentication fails. Don't show yet.
// Again, don't wait while preparing.
// Async part could be -  Reading a render template file. (File IO operation)
var AuthFailScreen = PrepareFailScreenAsync();

// Similar to failure screen.
var AuthSuccessScreen = PrepareSuccessScreenAsync();

Now, we use await to synchronize the flow to our liking -

var UserAuthResult = await UserAuthTask;
if (UserAuthResult.Success)
{
    var SuccessScreen = await AuthSuccessScreen;
    SuccessScreen.Show();
}
else
{
    var FailScreen = await AuthFailScreen;
    FailScreen.Show();
}

You could await each of the tasks before, like var UserAuthTask = await LoginAsync();, but then you would lose the benefit of doing multiple things which could've been done while authenticating. I am not sure about exception handling, but mix and match of async and sync code will likely cause issues.

Please correct me if I am wrong at any point. Thanks.