2016-03-21 7 views
1

Ich habe eine Methode, die einen Benutzer anmeldet und versucht, den Benutzer zu aktualisieren, wenn der viewModel sich von user Objekt unterscheidet. Das Verhalten, das ich sehe, ist verwirrend.Warum schlägt meine UpdateAsync (user) -Methode beim ersten Mal fehl, und behauptet, Benutzer existiert nicht?

Jedes Mal, wenn die Methode ausgeführt wird, wenn die user nicht vorher angemeldet wurde, schlägt die Linie await _userManager.UpdateAsync(user); mit Ausnahme: "There is no user with id: 99" (oder was auch immer id Wert). Wenn jedoch die user zuvor angemeldet war, funktioniert die Linie.

So zum Beispiel,

  1. Benutzer starten die App (momentan nicht angemeldet) und klickt Schaltfläche viewModel zum Server zu senden.
  2. Die App protokolliert die user in,
  3. Wenn die viewModel anders als die bestehenden user ‚s Daten vorhanden sind, versucht der Server, um es zu aktualisieren.
  4. Dieses Update schlägt mit "There is no user with id: 99"
  5. Benutzer klickt erneut und sendet die gleichen Daten auf dem Server. (Diesmal user wurde von früheren gescheiterten Post angemeldet)
  6. viewModel als die bestehenden Daten noch anders ist (denken Sie daran, das Update fehlgeschlagen letzte Mal)
  7. await _userManager.UpdateAsync(user); Werke und der Datensatz aktualisiert wird.

Im folgenden ist die Methode:

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

ich, dass ein Benutzer mit der ID 99 tut in der Tat in der Datenbank vorhanden sein bestätigen kann.

Für die Aufzeichnung ist das folgende der Inhalt von 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 

Ich denke, ein Hinweis auf die folgende Abfrage sein könnte (abgefangen mit SQL Server Profiler), die vor dem Update ausgeführt wird:

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]' 

Hier können wir sehen, dass @DynamicFilterParam_1=NULL. Die Variable @DynamicFilterParam_1 entspricht dem Wert für [Extent1].[TenantId]. Wenn ich manuell den Wert 2 (der Wert ist, der dem Datensatz in der Datenbank zugeordnet ist) statt NULL zuweisen und die Abfrage erneut ausführen, wird der Datensatz wie erwartet zurückgegeben.

Wenn ich die Methode zum zweiten Mal ausführen, kann ich sehen, daß TenantId korrekt, den Wert von 2.

zugeordnet Deshalb wird der Wert auf TenantId NULL entspricht das erste Mal zugewiesen wird? Warum schlägt die Methode jedes Mal fehl? Was kann ich tun, damit es funktioniert?

Als Antwort auf die Anfrage unten, ist die Definition von UpdateAsync in the asp.net boilerplate github

+2

pls den Code UpdateAsync Post – Viru

+0

Ich habe einen Link auf die Quelle für UpdateAsync hinzugefügt. Es ist auf GitHub. – DaveDev

+0

Hallo, @DaveDev, ich habe gerade das gleiche Problem. Haben Sie zufällig eine Lösung gefunden? – Vlad

Antwort

0

Dies wegen Multitenancy ist.ABP kennt die TenantId vor der Benutzeranmeldung nicht. Sie sollten die Mandanten-ID manuell festlegen, um zum Mandanten des Benutzers zu wechseln. Mein Login Der Code ist wie folgt aus:

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

Entscheidend ist die Verwendung von (UnitOfWorkManager.Current.SetTenantId(tenantId)) Anweisung

+0

Legende. Vielen Dank. – DaveDev