简体   繁体   中英

Has ASP.NET Identity UserManager.CreateAsync() been updated with breaking changes recently?

A few months ago I created my own implementation of ASP.NET Identity, overriding the UserStore to use dapper and a custom sql connection instead of Entity Framework. It worked fine at the time.

Now I updated all of the nuget packages today and I've been fighting problems since. Primarily when I register a new user by calling var result = await UserManager.CreateAsync(user, newAccount.Password); It creates the user and performs all other checks just fine, but then throws a bizarre error saying Invalid operation. The connection is closed. Invalid operation. The connection is closed.

Its as though UserManager.CreateAsync has a new method that needs to be overridden, but I have absolutely no idea what it could be.

For reference, here are parts of my implementation:

Account Controller:

        [Authorize]
            public class AccountController : Controller
            {

                public UserManager<User> UserManager { get; private set; }
                public UserTokenProvider UserTokenProvider { get; set; }

                public AccountController() : this(new UserManager<User>(new UserStore(ConfigurationManager.ConnectionStrings["DBConn"].ConnectionString)))
                {
                }

                public AccountController(UserManager<User> userManager)
                {
                    UserManager = userManager;
                    UserManager.PasswordHasher = new NoPasswordHasher();

                }

...

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Register(RegistrationModel newAccount)
        {
            try
            {
                if (DbConfig.MaintenanceMode) return RedirectToAction("ComingSoon", "Home");
                if (ModelState.IsValid)
                {
                    var user = new User(newAccount);

                    var result = await UserManager.CreateAsync(user, newAccount.Password);
                    if (result.Succeeded)
                    {
                        await SignInAsync(user, isPersistent: false);
                        var userIn = await UserManager.FindByEmailAsync(newAccount.UserName);
                        if (!userIn.EmailConfirmed)
                        {
                            await SendValidationEmail(userIn);
                            return RedirectToAction("ConfirmationSent", new {userName = user.UserName});
                        }
                        return RedirectToAction("Index", "Home");
                    }
                    else
                    {
                        AddErrors(result);
                    }
                }

                // If we got this far, something failed, redisplay form
                return View(newAccount);
            }
            catch (Exception ex)
            {
                var msg = ex.Message;

                return View(newAccount);
            }
        }

User Store:

    public class UserStore : IUserStore<User>, IUserLoginStore<User>, IUserPasswordStore<User>, IUserSecurityStampStore<User>, IUserRoleStore<User>, IUserEmailStore<User>
        {
            private readonly string _dbConn;


            public UserStore(string conn = null)
            {
                if (conn != null)
                    _dbConn = conn;
                else
                    _dbConn = DbConfig.ConnectionString;
            }

            public void Dispose()
            {
            }



            public virtual Task CreateAsync(User user)
            {
                using (var _conn = new SqlConnection(_dbConn))
                {
                    if (_conn.State == ConnectionState.Closed) _conn.Open();
                    return _conn.ExecuteAsync("users_UserCreate",
                        new
                        {
                            @UserId = user.Id,
                            @UserName = user.UserName,
                            @PasswordHash = user.PasswordHash,
                            @SecurityStamp = user.SecurityStamp
                        }, commandType: CommandType.StoredProcedure);

                }
            }

... Remaining methods omitted for brevity ...

You'll notice that the UserStore.CreateAsync() function has if (_conn.State == ConnectionState.Closed) _conn.Open(); as this was the suggestion from multiple threads regarding the connection closed error. Even without this line the query works fine and inserts a new user into the database correctly.

The error is coming from somewhere after UserStore.CreateAsync() is called by UserManager.CreateAsync().

Any idea what's missing?

The answer is no, ASP.NET Identity hasn't altered with breaking changes.

Using DotNetPeek I had a look at the Identity library to see what methods were being called during UserManager.CreateAsync() and it only calls UserStore.CreateSync and a password update.

After playing around with the code some more it dawned on me that whilst UserManager.CreateSync is awaited, the internal calls to UserStore.CreateSync isn't. Tanks to having to override public virtual Task CreateAsync(User user) and having to return a task that isn't awaited, we have to juggle some code to await the response from Dapper before returning it as part of a task again.

So, here's the updated UserStore.CreateAsync override that works. Note: if (_conn.State == ConnectionState.Closed) _conn.Open(); isn't actually needed as the connection was being closed before the method had finished, Dapper does a brilliant job of handling connections for you.

public virtual Task CreateAsync(User user)
{
    using (var _conn = new SqlConnection(_dbConn))
    {
        var result = _conn.ExecuteAsync("users_UserCreate", new
                    {
                        @UserId = user.Id,
                        @UserName = user.UserName,
                        @PasswordHash = user.PasswordHash,
                        @SecurityStamp = user.SecurityStamp
                    }, commandType: CommandType.StoredProcedure).ConfigureAwait(true);

        return Task.FromResult(result);
    }
}

Hopefully this will help someone else in the future facing the same sort of problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM