Our website uses MVC and identity framework 2.0. On the live site (fortunately) users can now log in perfectly:
Here's the code which makes this work attached to the Login action of the Account controller:
public async Task<ActionResult> Login(vmAccountLogin model, string ReturnUrl)
{
// if problems with model, just redisplay form
if (!ModelState.IsValid)
{
return View(model);
}
// hangs on this line
var user = UserManager.FindByEmail(model.Email);
// if user not found for this email, abort with no clues
if (user == null)
When I build the project and copy the DLL to the live server, it works perfectly. BUT ...
On my test version, this hangs on the line shown. After about ten seconds debugging jumps to the Dispose method. I've gone through the web.config file line by line to check they're the same. I've read many SO articles, but don't think any of them apply to this problem. Here's the UserManager call, by the way, which works fine:
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
If I enable debugging of .NET symbols, it hangs on the FindByEmail method call. The GetUserManager call shows this in the immediate window:
?HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
{wiseowl.ApplicationUserManager}
ClaimsIdentityFactory: {Microsoft.AspNet.Identity.ClaimsIdentityFactory<wiseowl.Models.ApplicationUser, string>}
DefaultAccountLockoutTimeSpan: {00:05:00}
EmailService: {wiseowl.EmailService}
MaxFailedAccessAttemptsBeforeLockout: 5
PasswordHasher: {Microsoft.AspNet.Identity.PasswordHasher}
PasswordValidator: {Microsoft.AspNet.Identity.PasswordValidator}
SmsService: {wiseowl.SmsService}
Store: {Microsoft.AspNet.Identity.EntityFramework.UserStore<wiseowl.Models.ApplicationUser>}
SupportsQueryableUsers: true
SupportsUserClaim: true
SupportsUserEmail: true
SupportsUserLockout: true
SupportsUserLogin: true
SupportsUserPassword: true
SupportsUserPhoneNumber: true
SupportsUserRole: true
SupportsUserSecurityStamp: true
SupportsUserTwoFactor: true
TwoFactorProviders: Count = 2
UserLockoutEnabledByDefault: true
UserTokenProvider: {Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider<wiseowl.Models.ApplicationUser>}
UserValidator: {Microsoft.AspNet.Identity.UserValidator<wiseowl.Models.ApplicationUser>}
Users: {System.Data.Entity.DbSet<wiseowl.Models.ApplicationUser>}
_claimsFactory: {Microsoft.AspNet.Identity.ClaimsIdentityFactory<wiseowl.Models.ApplicationUser, string>}
_defaultLockout: {00:05:00}
_disposed: false
_factors: Count = 2
_passwordHasher: {Microsoft.AspNet.Identity.PasswordHasher}
_passwordValidator: {Microsoft.AspNet.Identity.PasswordValidator}
_userValidator: {Microsoft.AspNet.Identity.UserValidator<wiseowl.Models.ApplicationUser>}
Does anyone have any suggestions please?
So far the hanging on the test environment looks like a mixing of async and blocking calls which can lead to deadlocks. FindByEmail
is an extension method that wraps the async API call of the UserManager.FindByEmailAsync
.
The source code for that version found in UserManagerExtensions.cs shows that the async call is being executed synchronously.
/// <summary>
/// Find a user by email
/// </summary>
/// <param name="manager"></param>
/// <param name="email"></param>
/// <returns></returns>
public static TUser FindByEmail<TUser, TKey>(this UserManager<TUser, TKey> manager, string email)
where TKey : IEquatable<TKey>
where TUser : class, IUser<TKey>
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
return AsyncHelper.RunSync(() => manager.FindByEmailAsync(email));
}
I believe that is causing a deadlock (hang) when run in the test environment. Does not explain why it still works in the live environment though.
I suggest making the code async all the way through via the FindByEmailAsync
method as it was intended. Try to avoid mixing async and blocking calls.
public async Task<ActionResult> Login(vmAccountLogin model, string ReturnUrl) {
// if problems with model, just redisplay form
if (!ModelState.IsValid) {
return View(model);
}
// hangs on this line
var user = await UserManager.FindByEmailAsync(model.Email);
// if user not found for this email, abort with no clues
if (user == null)
//...code removed for brevity
Outside of that I would also suggest making sure that the test environment is using the correct connection string for the identity data store. As the hang may also be due to a timeout as it waits to connect to the database.