1

我有一种方法可以让用户登录并尝试在用户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. 用户再次单击按钮并将相同的数据发布到服务器。(这次user是从之前失败的帖子登录的)
  6. viewModel仍然与现有数据不同(记住,上次更新失败)
  7. await _userManager.UpdateAsync(user);工作并且记录被更新。

以下是方法:

[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 为 99 的用户确实存在于数据库中。

为了记录,以下是内容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(这是与数据库中的记录关联的值)而不是NULL重新运行查询,它会按我的预期返回记录。

当我第二次执行该方法时,我可以看到 TenantId 被正确分配了值 2。

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

为了响应下面的请求,在asp.net 样板 github中提供了 UpdateAsync 的定义

4

1 回答 1

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

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

于 2016-11-04T10:56:19.773 回答