Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQL write to ASP.NET user table doesn't save

My setup:

  • ASP.NET 4.5 web api (on Azure) saving data to SQL db (also on Azure)
  • AngularJS web front end (another Azure web site)

When a user first signs up, I show them a "getting started intro". The intro is only supposed to run once - I log the timestamp of the intro launch date as a custom field in the ASP.NET user table.

Imagine my surprise when I log in (as a user would) and see the intro TWICE.

The AngularJS front end is properly sending the "intro viewed" message to the ASP.NET api, and the api responds with a success message. However, when I look at the raw data in the db, the timestamp is most definitely NOT updated. Consequently, the user will see the intro a second time (at which point the timestamp gets recorded in the db properly).

I have a crappy workaround. After the client requests an OAuth Bearer token from my server, the client then requests user information (to decide whether or not to show the tour). Waiting 100ms and then sending the "tour viewed" message back to the server masks the issue.

I've not seen ANY other issues storing data at any point. Because our db is on Azure, I can't hook up Profiler and the built in auditing doesn't give me any clues.

Is there something about requesting the token that leaves ASP.NET identity in a funny state? And it takes a brief wait before you can write to the table? Are custom fields that extend the base Identity setup prone to problems like this? Is the UserManager possibly doing something weird in its black box?

Does anyone have suggestions for how to continue debugging this problem? Or ever hear of anything like it?

Here's the relevant code that should be updating the "tour viewed" timestamp in the db:

    [HttpPost, Route("UserInfo")]
    public async Task<IHttpActionResult> UpdateUserInfo(UpdateBindingModel model)
    {
        var currentUser = UserManager.FindById(User.Identity.GetUserId());

        if (model.FirstName != null)
        {
            currentUser.FirstName = model.FirstName;
        }
        if (model.LastName != null)
        {
            currentUser.LastName = model.LastName;
        }
        if (model.SetIntroViewCompleteDate)
        {
            currentUser.IntroViewCompleteDate = DateTime.UtcNow;
        }
        if (model.SetIntroViewLaunchDate)
        {
            currentUser.IntroViewLaunchDate = DateTime.UtcNow;
        }
        if (model.SetTipTourCompleteDate)
        {
            currentUser.TipTourCompleteDate = DateTime.UtcNow;
        }
        if (model.SetTipTourLaunchDate)
        {
            currentUser.TipTourLaunchDate = DateTime.UtcNow;
        }

        IdentityResult result = await UserManager.UpdateAsync(currentUser);
        if (result.Succeeded)
        {
            var data = new UserInfoViewModel
            {
                FirstName = currentUser.FirstName,
                LastName = currentUser.LastName,
                IntroViewLaunchDate = currentUser.IntroViewLaunchDate
            };

            return Ok(data);
        }

        return InternalServerError();
    }

UPDATE ********* 4/18

I've also tried to move completely away from UserManager stuff. I've tried the following modifications (pulling the user data from a table like I would access any other data), but it still behaves the same. I'm starting to think that putting custom fields on the ApplicationUser object is a bad idea...

New db retrieve and save looks like this:

 ApplicationDbContext newContext = new ApplicationDbContext();
 var currentUser = await (from c in newContext.Users
                          where c.Email == User.Identity.Name
                          select c).SingleOrDefaultAsync();

 //update some values

 await newContext.SaveChangesAsync();
like image 789
waffles Avatar asked Apr 14 '16 00:04

waffles


1 Answers

Basically the problem might be with initialization of the `UserManager' and the fact that this class works on the db context so you need to persist changes to that context. Here is an example:

var userStore = new UserStore<ApplicationUser>(new MyDbContext());
var userManager = new UserManager(userStore);

That way you remember both manager and context. Then in your method you would normally call:

IdentityResult result = await userManager.UpdateAsync(currentUser);

followed by persisting this change to db context:

var dbContext = userStore.context;
dbContext.saveChanges();
like image 174
Lesmian Avatar answered Oct 15 '22 17:10

Lesmian