2016-03-21 5 views
1

У меня есть метод, который подписывает пользователя и пытается обновить пользователя, если viewModel отличается от объекта user. Поведение, которое я вижу, сбивает с толку.Почему мой метод UpdateAsync (пользователь) не работает в первый раз, заявив, что пользователь не существует?

Каждый раз, когда выполняется метод, если user ранее не вошли в систему, линия await _userManager.UpdateAsync(user); терпит неудачу за исключением: "There is no user with id: 99" (или любой другой id значение). Однако, если userбыл зарегистрирован, линия работает.

Так, например,

  1. Пользователь запускает приложение (в настоящее время не вошли в систему) и нажимает кнопку, чтобы получить возможность отправлять viewModel к серверу.
  2. приложение регистрирует user в,
  3. Если viewModel отличается от существующих данных user «s, сервер пытается обновить его.
  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); 
    } 
} 

Я могу подтвердить, что пользователь с идентификатором 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'[email protected]' 

Здесь мы можем видеть, что @DynamicFilterParam_1=NULL. Переменная @DynamicFilterParam_1 соответствует значению для [Extent1].[TenantId]. Если я вручную присвою значение 2 (которое является значением, связанным с записью в db) вместо NULL и повторит запрос, он возвращает запись, как я ожидаю.

Когда я выполнить метод во второй раз, я вижу, что TenantId правильно присваивается значение 2.

Почему значение, соответствующее TenantId быть назначен NULL в первый раз? Почему метод UpdateAsync терпит неудачу каждый раз? Что я могу сделать, чтобы он работал?

В ответ на просьбу ниже, определение UpdateAsync доступен в the asp.net boilerplate github

+2

пожалуйста вывесить код UpdateAsync – Viru

+0

Я добавил ссылку на источник UpdateAsync. Это на GitHub. – DaveDev

+0

Привет, @DaveDev, сейчас я сталкиваюсь с той же проблемой. Вы нашли решение случайно? – Vlad

ответ

0

Это из-за Multitenancy.ABP не знает TenantId перед входом пользователя. Вы должны вручную установить идентификатор арендатора, чтобы переключиться на арендатора пользователя. Мой Логин код так:

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)) заявления

+0

Легенда. Благодарю. – DaveDev