【问题标题】:Has ASP.NET Identity UserManager.CreateAsync() been updated with breaking changes recently?ASP.NET Identity UserManager.CreateAsync() 最近是否更新了重大更改?
【发布时间】:2017-11-14 09:51:11
【问题描述】:

几个月前,我创建了自己的 ASP.NET Identity 实现,重写了 UserStore 以使用 dapper 和自定义 sql 连接而不是 Entity Framework。当时效果很好。

现在我今天更新了所有的 nuget 包,从那以后我一直在与问题作斗争。主要是当我通过调用var result = await UserManager.CreateAsync(user, newAccount.Password);注册新用户时,它会创建用户并执行所有其他检查,但随后会抛出一个奇怪的错误,说Invalid operation. The connection is closed.

好像 UserManager.CreateAsync 有一个需要重写的新方法,但我完全不知道它可能是什么。

作为参考,以下是我的部分实现:

帐户管理员:

        [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);
            }
        }

用户商店:

    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 ...

您会注意到 UserStore.CreateAsync() 函数具有if (_conn.State == ConnectionState.Closed) _conn.Open();,因为这是多个线程关于连接关闭错误的建议。即使没有这一行,查询也能正常工作并正确地将新用户插入数据库。

错误来自 UserManager.CreateAsync() 调用 UserStore.CreateAsync() 之后的某个地方。

知道缺少什么吗?

【问题讨论】:

    标签: c# asp.net asp.net-mvc asp.net-identity dapper


    【解决方案1】:

    答案是否定的,ASP.NET 标识并未因重大更改而改变。

    使用 DotNetPeek,我查看了 Identity 库,了解在 UserManager.CreateAsync() 期间调用了哪些方法,它只调用 UserStore.CreateSync 和密码更新。

    在玩了一些代码之后,我突然意识到,在等待 UserManager.CreateSync 时,对 UserStore.CreateSync 的内部调用不是。坦克不得不覆盖 public virtual Task CreateAsync(User user) 并不得不返回一个没有等待的任务,我们必须处理一些代码来等待来自 Dapper 的响应,然后再将其作为任务的一部分返回。

    所以,这是更新后的 UserStore.CreateAsync 覆盖。注意:if (_conn.State == ConnectionState.Closed) _conn.Open(); 实际上并不需要,因为在方法完成之前连接已关闭,Dapper 为您处理连接做得非常出色。

    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);
        }
    }
    

    希望这将有助于将来面临同样问题的其他人。

    【讨论】:

    • 在 netcore 2.1.302 上,如果我们不等待 CreateAsync 执行,它会在 Azure 和本地 SqlServer 中引发异常。在这种情况下,它是一个在 for 循环中具有 await _roleManager.CreateAsync(role); 的 Seed 类。我尝试使用 DataContext 添加角色并注意到:异步仅将第一个角色保存在列表中(奇怪);没有异步工作正常,所有角色都保存在 AspNetRoles 表中。使 CreateAsync 与 SqlServer 一起工作的另一种方法是编辑 Startup.cs 和 Wait() 种子执行 mySeeder.SeedUsers().Wait();。这里的 SeedUsers 是一个自定义方法,它为角色和用户运行 CreateAsync。
    猜你喜欢
    • 2013-02-15
    • 1970-01-01
    • 2015-05-31
    • 2011-04-16
    • 1970-01-01
    • 1970-01-01
    • 2019-07-04
    • 2014-05-15
    • 1970-01-01
    相关资源
    最近更新 更多