为什么我的UpdateAsync(用户)方法第一次失败,声称用户不存在?

时间:2016-03-21 11:26:27

标签: c# entity-framework asp.net-boilerplate

如果viewModeluser对象不同,我有一个方法可以对用户进行签名并尝试更新用户。我所看到的行为令人困惑。

每次执行该方法时,如果先前未登录user,则行await _userManager.UpdateAsync(user);将失败,并显示例外:"There is no user with id: 99"(或任何id值)。但是,如果先前已登录user ,则该行有效。

例如,

  1. 用户启动应用程序(当前未登录)并单击按钮将viewModel发布到服务器。
  2. 该应用会记录user
  3. 如果viewModel与现有user的数据不同,则服务器会尝试更新它。
  4. 此更新将因"There is no user with id: 99"
  5. 而失败
  6. 用户再次点击按钮并将相同的数据发布到服务器。 (此时user已从先前失败的帖子中登录)
  7. viewModel仍然与现有数据不同(请记住,上次更新失败)
  8. await _userManager.UpdateAsync(user);正常工作且记录已更新。
  9. 以下是方法:

    [UnitOfWork]
    public async Task<AjaxResponse> Post(MyViewModel viewModel)
    {
        try
        {
            var loginResult = await _userManager.LoginAsync(viewModel.UserName, viewModel.Password, viewModel.TenancyName);
    
            User user;
            if (loginResult.Result == AbpLoginResultType.Success)
            {
                await SignInAsync(loginResult.User, loginResult.Identity);
    
                user = loginResult.User;
    
                if (user.AccessToken != viewModel.AccessToken)
                {
                    user.AccessToken = viewModel.AccessToken;
    
                    // why does this fail the first time?
                    await _userManager.UpdateAsync(user);
                }
            }
            else 
            { 
                /* do some other UnitOfWork stuff below */
            }
    
    
            return new AjaxResponse(new MyResult
            {
                Name = user.Name + " " + user.Surname,
                UserName = user.UserName,
                EmailAddress = user.EmailAddress,
                IsActive = user.IsActive,
                Success = true,
                UserId = user.UserId,
            });
        }
        catch (Exception ex)
        {
            throw new HttpException((int)HttpStatusCode.InternalServerError, ex.Message);
        }
    }
    

    我可以确认数据库中确实存在ID为的用户

    对于记录,以下是ex.StackTrace

    的内容
       at Abp.Authorization.Users.AbpUserManager`3.<GetUserByIdAsync>d__5b.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at Abp.Authorization.Users.AbpUserManager`3.<UpdateAsync>d__64.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
       at MyProject.Web.Controllers.Api.AccountApiController.<Post>d__16.MoveNext() in C:\dev\MyProject\MyProject.Web\Controllers\Api\AccountApiController.cs:line 146
    

    我认为在更新之前执行的以下查询(使用SQL Server Profiler拦截)可能会出现一条线索:

    exec sp_executesql N'SELECT TOP (1) 
        [Extent1].[Id] AS [Id], 
        [Extent1].[AccessToken] AS [AccessToken], 
        [Extent1].[UserId] AS [UserId], 
        [Extent1].[EmailAddress] AS [EmailAddress], 
        [Extent1].[TenantId] AS [TenantId], 
        [Extent1].[IsDeleted] AS [IsDeleted], 
        -- irrelevant stuff removed
        FROM [dbo].[AbpUsers] AS [Extent1]
        WHERE 
                ((([Extent1].[TenantId] IS NULL) AND (@DynamicFilterParam_1 IS NULL)) 
            OR  (([Extent1].[TenantId] IS NOT NULL) AND ([Extent1].[TenantId] = @DynamicFilterParam_1)) 
            OR (@DynamicFilterParam_2 IS NOT NULL)) AND (([Extent1].[IsDeleted] = @DynamicFilterParam_3) 
            OR (@DynamicFilterParam_4 IS NOT NULL)) AND ([Extent1].[EmailAddress] = @p__linq__0)',
            N'@DynamicFilterParam_1 int,@DynamicFilterParam_2 bit,@DynamicFilterParam_3 bit,@DynamicFilterParam_4 bit,@p__linq__0 nvarchar(4000)'
            ,@DynamicFilterParam_1=NULL,@DynamicFilterParam_2=NULL,@DynamicFilterParam_3=0,@DynamicFilterParam_4=NULL,@p__linq__0=N'myemail@mail.com'
    

    在这里,我们可以看到@DynamicFilterParam_1=NULL。变量@DynamicFilterParam_1对应[Extent1].[TenantId]的值。如果我手动分配值2(这是与db中的记录关联的值)而不是NULL并重新运行查询,它将返回我期望的记录。

    当我第二次执行该方法时,我可以看到TenantId被正确赋值为2.

    为什么TenantId对应的值第一次被赋值为NULL?为什么UpdateAsync方法每次都会失败?我能做些什么才能让它发挥作用?

    为响应下面的请求,可以在the asp.net boilerplate github

    中找到UpdateAsync的定义

1 个答案:

答案 0 :(得分:0)

这是因为多租户。用户登录前ABP不知道TenantId。您应手动设置租户ID以切换到用户的租户。我的登录代码是这样的:

public virtual async Task<JsonResult> Login(LoginViewModel loginModel, string returnUrl = "", string returnUrlHash = "")
{
    var loginResult = await GetLoginResultAsync(loginModel.UsernameOrEmailAddress, loginModel.Password, loginModel.TenancyName);

    var tenantId = loginResult.Tenant == null ? (int?)null : loginResult.Tenant.Id;

    using (UnitOfWorkManager.Current.SetTenantId(tenantId))
    {
        if (loginResult.User.ShouldChangePasswordOnNextLogin)
        {
            loginResult.User.SetNewPasswordResetCode();

            return Json(new AjaxResponse
            {
                TargetUrl = Url.Action(
                    "ResetPassword",
                    new ResetPasswordViewModel
                    {
                        TenantId = tenantId,
                        UserId = SimpleStringCipher.Instance.Encrypt(loginResult.User.Id.ToString()),
                        ResetCode = loginResult.User.PasswordResetCode
                    })
            });
        }

        var signInResult = await _signInManager.SignInOrTwoFactorAsync(loginResult, loginModel.RememberMe);
        if (signInResult == SignInStatus.RequiresVerification)
        {
            return Json(new AjaxResponse
            {
                TargetUrl = Url.Action(
                    "SendSecurityCode",
                    new
                    {
                        returnUrl = returnUrl + (returnUrlHash ?? ""),
                        rememberMe = loginModel.RememberMe
                    })
            });
        }

        Debug.Assert(signInResult == SignInStatus.Success);

        await UnitOfWorkManager.Current.SaveChangesAsync();

        if (string.IsNullOrWhiteSpace(returnUrl))
        {
            returnUrl = GetAppHomeUrl();
        }

        if (!string.IsNullOrWhiteSpace(returnUrlHash))
        {
            returnUrl = returnUrl + returnUrlHash;
        }

        return Json(new AjaxResponse { TargetUrl = returnUrl });
    }
}

关键是使用(UnitOfWorkManager.Current.SetTenantId(tenantId))语句