1

I have this this code

ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password)

I want this code piece to run Synchronously, because my very next statement is depend upon this. Below call fails most of the time because user is null.

var roles = await userManager.GetRolesAsync(user.Id);

Any suggestion?

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
Sachin
  • 459
  • 1
  • 7
  • 22
  • How about to check ```if (user!=null){/* do your stuff*/}``` ? – tym32167 Dec 22 '16 at 10:30
  • 3
    Pretty sure this is nothing to do with being async, the `await` keyword should take care of pausing execution. Are you sure the call to get the ApplicationUser is succeeding? Sounds like it's returning null. – Equalsk Dec 22 '16 at 10:31
  • 5
    You don't need to do anything - the next line will run **ONLY** after `FindAsync` completes. if `roles` isn't what you expect, check for coding or data issues, not problems with async – Panagiotis Kanavos Dec 22 '16 at 10:31
  • 4
    Am I missing something? The `GetRolesAsync` will be executed after `FindAsync` is completed anyway. That's how `await` works. – Alex Dec 22 '16 at 10:32
  • 2
    How making it synchronously will help you ? – mybirthname Dec 22 '16 at 10:33
  • Make sure your `user` exists. When `Find` may return `null` when no user found. – pwas Dec 22 '16 at 10:38
  • The method will return `null` if there is no match. `await` has nothing to do with it. Either username or password is wrong – Panagiotis Kanavos Dec 22 '16 at 11:06
  • 1
    Why the upvotes to an obviously wrong and un-answerable question? – Panagiotis Kanavos Dec 22 '16 at 12:08
  • @Sachin: I think the confusion is that you want it to run *serially*, not *synchronously*. `await` is how you do asynchronous *serial* code, so it works just fine as-is. – Stephen Cleary Dec 22 '16 at 13:15

3 Answers3

3

There is nothing wrong with the code. await means that execution continues only after the asynchronous method that follows finishes. This means that in this snippet:

var user = await userManager.FindAsync(context.UserName, context.Password)
var roles = await userManager.GetRolesAsync(user.Id);

the call to GetRolesAsync will be executed only after the previous line completes.

From the documentation of UserManager.FindAsync

Returns a user with the specified username and password or null if there is no match.

Either the username or password are wrong. Just return a message to the end user asking them to retry. If you make this call using stored credentials, check them again.

In any case, you need to check for an authentication failure before trying to use the user value, eg:

var user = await userManager.FindAsync(context.UserName, context.Password)
if (user == null)
{
    //Somehow report failure, decrement retry counters, etc
    retries--;
    return false;
}
else 
{
    var roles = await userManager.GetRolesAsync(user.Id);
    ....
}
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
1
ApplicationUser user = userManager.FindAsync(context.UserName, context.Password).Result;
var roles = userManager.GetRolesAsync(user.Id).Result;

But if the context user just is null, it won't help you if you do it synchronously - user will be just as NULL as it was before.

If you are trying to add a login to your application, it might help to check out this.
Also, you should probably use policies instead of roles.

Edit:
OK, without deadlock-problem I was unaware of (thanks @hvd):

var user = Task.Run(() => userManager.FindAsync(context.UserName, context.Password)).Result;
var roles = Task.Run(() => userManager.GetRolesAsync(user.Id)).Result;

If the async method returns void

Task.Run( () => asyncMethod()).Wait();
Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
  • There's no need for this. The original code has no problem – Panagiotis Kanavos Dec 22 '16 at 10:30
  • @Panagiotis Kanavos: I know, but it's the answer to his question. – Stefan Steiger Dec 22 '16 at 10:31
  • 2
    @StefanSteiger The problem is [an XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). The *real* problem is `user` being `null`, not the code running asynchronously. Your answer should fail for the real problem in exactly the same way as the OP's own code, plus additionally, you've introduced the possibility of deadlock depending on where this code is called and how `FindAsync` is implemented. –  Dec 22 '16 at 10:58
  • FindAsync returns null if the username and password don't match. Using `.Result` will still return null – Panagiotis Kanavos Dec 22 '16 at 11:07
  • @Panagiotis Kanavos: I never said his problem would be solved by this - i just answered how to make it synchronous, which was his question ;) – Stefan Steiger Dec 22 '16 at 11:25
  • This code deadlocks on my single CPU azure build agent – Marc Wittke Mar 06 '18 at 19:30
0

I don't see why there could be problem - if you await properly, user should be properly fetched before GetRolesAsync call. Is this your whole code?

Mayby, user is null because it doesn't exist?

Besides, you can chain your calls instead of running it synchronously:

var roles = await userManager.FindAsync(context.UserName, context.Password)
                             .ContinueWith(task => 
                                              userManager.GetRolesAsync(task.Result.Id))
                             .Unwrap();
pwas
  • 3,225
  • 18
  • 40