78

I've added custom fields to the ApplicationUser class
I've also created a form through which the user can enter/edit the fields.
However for some reason I'm not able to update the fields in the database.

[HttpPost]
[ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Manage(EditProfileViewModel model)
{
    if (ModelState.IsValid)
    {
        // Get the current application user
        var user = User.Identity.GetApplicationUser();

        // Update the details
        user.Name = new Name { First = model.FirstName, Last = model.LastName, Nickname = model.NickName };
        user.Birthday = model.Birthdate;

        // This is the part that doesn't work
        var result = await UserManager.UpdateAsync(user);

        // However, it always succeeds inspite of not updating the database
        if (!result.Succeeded)
        {
            AddErrors(result);
        }
    }

    return RedirectToAction("Manage");
}

My problem is similar to MVC5 ApplicationUser custom properties, but that seems to use an older version of Identity because the IdentityManager class doesn't seem to exist.

Can someone guide me on how to update User info in the database?

UPDATE: If I include all the fields in the register form, all the values are stored in the appropriate field in a new record of the Users table from the database.

I don't know to make changes to the fields of an existing user (row in the users table). UserManager.UpdateAsync(user) doesn't work.

Also note my issue is more Identity oriented than EntityFramework

Community
  • 1
  • 1
galdin
  • 12,411
  • 7
  • 56
  • 71
  • Have you checked to perform Update-Database, after changes to ApplicationUser? Also "Name" is a Class Reference, it shall be serialized or mapped appropriately in the OnModelCreating. – jd4u Dec 08 '13 at 19:00
  • No there's no problem with that, the database structure is just the same as `ApplicationUser`. The fields of name is a column in the database (as Name_First, Name_Last and Name_NickName). My problem is with the file database not getting updated with the new values when I call `UserManager.UpdateAsync(user)`. I just want to know how I'm supposed to go about updating the ApplicationUser (Users table) – galdin Dec 09 '13 at 19:56
  • ApplicationUser and OnModelCreating code snippets can be useful to help solve this issue. From your comment, it seems to be mapping issue for the Name property and its parts. – jd4u Dec 10 '13 at 08:47
  • @jd4u By *updating the ApplicationUser (Users table)* I meant the values in the table and not the structure of the table. Mappings are proper and are working with the register form. The info just can't be updated. – galdin Dec 10 '13 at 12:55

12 Answers12

102

OK... I spent hours trying to figure why userManager.updateAsync would not persist the user data that we edit ... until I reached the following conclusion:

The confusion arises from the fact that we create the UserManager in one line like this:

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

...then we use manager.UpdateAsync( user ); but that will update the user in the context, and then we will need to save changes to the dbcontext of the Identity. So, the question is how to get the Identity DBcontext in the easiest way.

To solve this, we should not create the UserManager in one line ... and here is how I do it:

var store = new UserStore<ApplicationUser>(new MyDbContext());
var manager = new UserManager(store);

then after updating the user by calling

manager.UpdateAsync(user);

then you go to the context

var ctx = store.context;

then

ctx.saveChanges();

wahooooooo...persisted :)

Hope this will help someone who pulled their hair for a few hours :P

Ben
  • 54,723
  • 49
  • 178
  • 224
stackunderflow
  • 3,811
  • 5
  • 31
  • 43
  • 4
    shortcut to the last two step : store.context.saveChanges(); – stackunderflow Mar 19 '14 at 16:34
  • 3
    finally .. for those who need full implementation this link is excellent http://weblogs.asp.net/imranbaloch/archive/2013/12/12/a-simple-implementation-of-microsoft-aspnet-identity.aspx – stackunderflow Mar 19 '14 at 17:28
  • 2
    I think it's wrong to call updateAsync and then two lines save context changes. Won't that present a race condition? – mac10688 Dec 13 '14 at 17:44
  • 1
    You mean that we should use `await` – stackunderflow Dec 15 '14 at 11:01
  • 1
    Is it really necessary to to do ctx.saveChanges(); after UpdateAsync()? I don't think so. And don't forget close put "new MyDbContext()" to using block. – Alexander May 22 '15 at 12:43
  • Alexander, this isnt primarily a asp.net identity issue; yet it is entity framework rule of thumb that you call `saveChanges()` on the `DbContext`. So that any update to the DbContext is saved from memory to disk – stackunderflow May 23 '15 at 22:15
  • @mac10688 ASP.NET identity saves the database context for you it seems: https://github.com/aspnet/Identity/blob/dev/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs#L114. Called through: `UserManager#UpdateAsync(TUser)` -> `UserManager#UpdateUserAsync(TUser)` -> `UserStore#UpdateAsync(TUser, CancellationToken)` -> `UserStore#SaveChanges(CancellationToken)`. – Zero3 Apr 04 '16 at 11:48
  • @Zero3, correct, you linked to the v3 source, here's the source for v2.1, where the EF store uses SaveChanges() unded the hood: https://aspnetidentity.codeplex.com/SourceControl/latest#src/Microsoft.AspNet.Identity.EntityFramework/UserStore.cs – Bart Verkoeijen Sep 01 '16 at 10:15
  • "The confusion arises from the fact that we create the UserManager in one line like this...To solve this, we should not create the UserManager in one line..." *Where* are you talking about? – RaelB Sep 25 '16 at 10:20
  • I am doing this directly in my `ExternalLoginCallback`-Method. Data are written to DB without calling `ctx.SaveChanges()`. But upon redirection to my `HomeController`, the `UserManager` still contains the old data?! Any idea why?!Logging off and on reloads the data. I had long hair once ... rage pulled them out x_X Btw.: Im am using the up to date Visual Studio 2015 MVC core Example project with individual user accounts to establish a Steam OpenID login... – DoubleVoid Nov 02 '16 at 21:38
62

If you leave any of the fields for ApplicationUser OR IdentityUser null the update will come back as successful but wont save the data in the database.

Example solution:

ApplicationUser model = UserManager.FindById(User.Identity.GetUserId())

Add the newly updated fields:

model.Email = AppUserViewModel.Email;
model.FName = AppUserViewModel.FName;
model.LName = AppUserViewModel.LName;
model.DOB = AppUserViewModel.DOB;
model.Gender = AppUserViewModel.Gender;

Call UpdateAsync

IdentityResult result = await UserManager.UpdateAsync(model);

I have tested this and it works.

Jammer
  • 9,969
  • 11
  • 68
  • 115
JoshdeVries
  • 887
  • 7
  • 9
14

The OWIN context allows you to get the db context. Seems to be working fine so far me, and after all, I got the idea from the ApplciationUserManager class which does the same thing.

    internal void UpdateEmail(HttpContext context, string userName, string email)
    {
        var manager = context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var user = manager.FindByName(userName);
        user.Email = email;
        user.EmailConfirmed = false;
        manager.Update(user);
        context.GetOwinContext().Get<ApplicationDbContext>().SaveChanges();
    }
Atters
  • 801
  • 8
  • 19
  • 4
    I don't think the last line is required any longer (this question was asked when Identity was new). I just checked source control of a recent project and noticed that I only did `await UserManager.UpdateAsync(user)` and the change persisted. Could you please confirm? – galdin Jun 07 '15 at 13:20
  • 1
    I can confirm that for this example the last line was most definitely required to persist the changes to the database. This is using the latest stable version of Identity 2 available for a webforms application. Perhaps the Async methods operate differently? – Atters Jun 08 '15 at 16:57
  • I am using Owin with IOC (with `Update()` called from inside a non-async helper function) and this is the only solution that worked with the standard Owin context. Cheers for saving my day and +1 – iCollect.it Ltd Oct 25 '15 at 22:43
5

The UserManager did not work, and As @Kevin Junghans wrote,

UpdateAsync just commits the update to the context, you still need to save the context for it to commit to the database

Here is quick solution (prior to new features in ASP.net identity v2) I used in a web forms projetc. The

class AspNetUser :IdentityUser

Was migrated from SqlServerMembership aspnet_Users. And the context is defined:

public partial class MyContext : IdentityDbContext<AspNetUser>

I apologize for the reflection and synchronous code--if you put this in an async method, use await for the async calls and remove the Tasks and Wait()s. The arg, props, contains the names of properties to update.

 public static void UpdateAspNetUser(AspNetUser user, string[] props)
 {
     MyContext context = new MyContext();
     UserStore<AspNetUser> store = new UserStore<AspNetUser>(context);
     Task<AspNetUser> cUser = store.FindByIdAsync(user.Id); 
     cUser.Wait();
     AspNetUser oldUser = cUser.Result;

    foreach (var prop in props)
    {
        PropertyInfo pi = typeof(AspNetUser).GetProperty(prop);
        var val = pi.GetValue(user);
        pi.SetValue(oldUser, val);
    }

    Task task = store.UpdateAsync(oldUser);
    task.Wait();

    context.SaveChanges();
 }
Mogsdad
  • 44,709
  • 21
  • 151
  • 275
subsci
  • 1,740
  • 17
  • 36
3

I also had problems using UpdateAsync when developing a version of SimpleSecurity that uses ASP.NET Identity. For example, I added a feature to do a password reset that needed to add a password reset token to the user information. At first I tried using UpdateAsync and it got the same results as you did. I ended up wrapping the user entity in a repository pattern and got it to work. You can look at the SimpleSecurity project for an example. After working with ASP.NET Identity more (documentation is still non-existent) I think that UpdateAsync just commits the update to the context, you still need to save the context for it to commit to the database.

Kevin Junghans
  • 17,475
  • 4
  • 45
  • 62
  • Yes `UpdateAsync just commits the update to the context, [...] save the context for it to commit to the database' – subsci Feb 08 '14 at 09:39
3

I have tried the functionality in the same way and when i call UserManager.Updateasync method it succeeds but there is no update in the database. After spending some time i found another solution to update the data in aspnetusers table which is following:

1) you need to create UserDbContext class inheriting from IdentityDbContext class like this:

public class UserDbContext:IdentityDbContext<UserInfo>
{
    public UserDbContext():
        base("DefaultConnection")
    {
        this.Configuration.ProxyCreationEnabled = false;
    }
}

2) then in Account controller update user information like this:

UserDbContext userDbContext = new UserDbContext();
userDbContext.Entry(user).State = System.Data.Entity.EntityState.Modified;
await userDbContext.SaveChangesAsync();

where user is your updated entity.

hope this will help you.

Nogard
  • 1,779
  • 2
  • 18
  • 21
Ankit Sahrawat
  • 1,306
  • 1
  • 11
  • 24
3

Excellent!!!

IdentityResult result = await UserManager.UpdateAsync(user);
Max
  • 538
  • 1
  • 6
  • 16
1

Based on your question and also noted in comment.

Can someone guide me on how to update User info in the database?

Yes, the code is correct for updating any ApplicationUser to the database.

IdentityResult result = await UserManager.UpdateAsync(user);

  • Check for constrains of all field's required values
  • Check for UserManager is created using ApplicationUser.

UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));

jd4u
  • 5,789
  • 2
  • 28
  • 28
  • aren't the constraints checked while checking `ModelState.IsValid` ? – galdin Dec 10 '13 at 13:35
  • ModelState checks for View Model. You are taking the ApplicationUser from Db using GetApplicationUser(). – jd4u Dec 10 '13 at 13:37
  • Moreover, UpdateManager.Update is just performing one validation for username in default implementation before sending update call to UserStore. Try using UserStore directly for the debug purpose. – jd4u Dec 10 '13 at 13:39
  • 2
    @anyone, While marking any answer not-useful, please read all and have a little context in mind. This answer is correct to the context. Moreover, the answer selected here is not a solution, its alternate approach ignoring the Identity Framework's core framework and directly working with database. – jd4u Feb 09 '14 at 06:45
0

This works for me. I'm using Identity 2.0, it looks like GetApplicationUser isn't there anymore.

        var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
        if (!string.IsNullOrEmpty(form["FirstName"]))
        {
            user.FirstName = form["FirstName"];
        }
        if (!string.IsNullOrEmpty(form["LastName"]))
        {
            user.LastName = form["LastName"];
        }
        IdentityResult result = await UserManager.UpdateAsync(user);
Yan
  • 321
  • 3
  • 3
0

I am using the new EF & Identity Core and I have the same issue, with the addition that I've got this error:

The instance of entity type cannot be tracked because another instance of this type with the same key is already being tracked.

With the new DI model I added the constructor's Controller the context to the DB.

I tried to see what are the conflict with _conext.ChangeTracker.Entries() and adding AsNoTracking() to my calls without success.

I only need to change the state of my object (in this case Identity)

_context.Entry(user).State = EntityState.Modified;
var result = await _userManager.UpdateAsync(user);

And worked without create another store or object and mapping.

I hope someone else is useful my two cents.

Ivan Paniagua
  • 312
  • 2
  • 10
-1

Add the following code to your Startup.Auth.cs file under the static constructor:

        UserManagerFactory = () => new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));

        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };

The UserManagerFactory setting line of code is what you use to associate your custom DataContext with the UserManager. Once you have done that, then you can get an instance of the UserManager in your ApiController and the UserManager.UpdateAsync(user) method will work because it is using your DataContext to save the extra properties you've added to your custom application user.

Chris Swain
  • 5,193
  • 5
  • 21
  • 22
  • Your code contains quite a few things irrelevant to the question. I suggest you clean it up. – Zero3 Apr 04 '16 at 09:52
-2

I am using .Net Core 3.1 or higher version.Please follow the solution:

  public class UpdateAssignUserRole
    {
        public string username { get; set; }
        public string rolename { get; set; }
        public bool IsEdit { get; set; }

    }

 private async Task UpdateSeedUsers(UserManager<IdentityUser> userManager, UpdateAssignUserRole updateassignUsername)
        {
            IList<Users> Users = await FindByUserName(updateassignUsername.username);

            if (await userManager.FindByNameAsync(updateassignUsername.username) != null)
            {
                var user = new IdentityUser
                {
                    UserName = updateassignUsername.username,
                    Email = Users[0].Email,
 
                };
                var result = await userManager.FindByNameAsync(updateassignUsername.username);
                if (result != null)
                {
                    IdentityResult deletionResult = await userManager.RemoveFromRolesAsync(result, await userManager.GetRolesAsync(result));
                    if (deletionResult != null)
                    {
                        await userManager.AddToRoleAsync(result, updateassignUsername.rolename);
                    }
                }
            }

        }
Papun Sahoo
  • 407
  • 5
  • 13
  • I think you've missed the OP's point - they were talking about custom fields/properties on the user, your example is assigning/removing Roles from a user when the user is being deleted/seeded, I don't quite see the correlation or how this in fact solves the OP's issue – Chris Jun 21 '21 at 14:48