2
votes

I am working on a webapplication with .NET Core 2 Razor Pages and individual accounts authentication.

I extended my db with firstname since it's not implemented bystandard. Everything is working properly. However, I'd like to extend my /Account/Manage page to make sure users are able to change their own name.

The OnGetAsync works fine, but when my OnPostAsync is not working as it should be.

OnPostAsync

 public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        var user = await _userManager.GetUserAsync(User);
        if (user == null)
        {
            throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
        }

        if (Input.Email != user.Email)
        {
            var setEmailResult = await _userManager.SetEmailAsync(user, Input.Email);
            if (!setEmailResult.Succeeded)
            {
                throw new ApplicationException($"Unexpected error occurred setting email for user with ID '{user.Id}'.");
            }
        }

        if (Input.PhoneNumber != user.PhoneNumber)
        {
            var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber);
            if (!setPhoneResult.Succeeded)
            {
                throw new ApplicationException($"Unexpected error occurred setting phone number for user with ID '{user.Id}'.");
            }
        }

        // not working yet
        if (Input.FirstName != user.FirstName)
        {
            var setFirstNameResult = await MyManager.SetFirstNameAsync(user, Input.FirstName);

            if (!setFirstNameResult.Succeeded)
            {
                throw new ApplicationException($"Unexpected error occurred setting first name for user with ID '{user.Id}'.");
            }
        }

        StatusMessage = "Your profile has been updated";
        return RedirectToPage();
    }

MyManager.cs

public class MyManager : UserManager<ApplicationUser>
{
    public MyManager(IUserStore<ApplicationUser> store, IOptions<IdentityOptions> optionsAccessor, IPasswordHasher<ApplicationUser> passwordHasher, IEnumerable<IUserValidator<ApplicationUser>> userValidators, IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<ApplicationUser>> logger) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
    {

    } 

    public static async Task<IdentityResult> SetFirstNameAsync(ApplicationUser user, string FirstName)
    {
        if (user == null)
        {
            throw new ArgumentNullException(nameof(user));
        }

        if (FirstName == null)
        {
            throw new ArgumentNullException(nameof(FirstName));
        }

        user.FirstName = FirstName;

        return IdentityResult.Success;
    }
}

It fails when I hit the 'Save' button and tells me my profile has been succesfully updated, but it did not. It just kept the old first name value. What am I missing here?

1
The GetUserAsync-method is in the framework, I have not implemented it by myself. The return value is the standard ApplicationUser object with my own custom added property of public string FirstName { get; set; }. I.e., the method retrieves the current ApplicationUser from the dbM. Douglas
Why you need such method? Existing methods for Phone number and email are here because those properties sometimes need to be confirmed so you update them after verifing the token received. On you post action just update your entities into your context and you will avoid multiple connexion/deconnexion to your database.CodeNotFound
I understand your point, but I'd like to avoid initializing a new dbContext in my clean InputModel.M. Douglas

1 Answers

2
votes

Your method doesn't do what the similar built-in methods do: namely, actually persist the change back to the database. You're simply setting the property on the user and then returning success. As soon as that instance goes out of scope, the value you set for first name goes with it.

That said, @CodeNotFound's comment is salient. There's no reason to do this, nor should you. The other methods are there for particular use cases. For just generally updating properties on your user, you should simply set the relevant property, and then use UpdateAsync to save the user back.