I have a method that signs a user in and attempts to update the user if the viewModel
is different than user
object. The behaviour I'm seeing is confusing.
Every time the method executes, if the user
was not previously logged in, the line await _userManager.UpdateAsync(user);
fails with the exception: "There is no user with id: 99"
(or whatever id
value). However, if the user
was previously logged in, the line works.
So for example,
- User starts up the app (not currently logged in) and clicks button to post
viewModel
to server. - The app logs the
user
in, - If the
viewModel
is different than the existinguser
's data, the server attempts to update it. - This update will fail with
"There is no user with id: 99"
- User clicks button again and posts the same data to the server. (this time
user
has been logged in from previous failed post) viewModel
is still different than existing data (remember, the update failed last time)await _userManager.UpdateAsync(user);
works and the record is updated.
The following is the method:
[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);
}
}
I can confirm that a user with the id 99 does in fact exist in the database.
For the record, the following is the contents of 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
I think one clue might be in the following query (intercepted with SQL Server Profiler) that gets executed before the update:
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'
Here, we can see that @DynamicFilterParam_1=NULL
. The variable @DynamicFilterParam_1
corresponds to the value for [Extent1].[TenantId]
. If I manually assign the value 2
(which is the value associated with the record in the db) instead of NULL
and rerun the query, it returns the record as I expect.
When I execute the method the second time, I can see that TenantId is correctly assigned the value of 2.
Why is the value corresponding to TenantId being assigned NULL the first time? Why does the UpdateAsync
method fail every first time? What can I do to make it work?
In response to the request below, the definition of UpdateAsync is available in the asp.net boilerplate github