locked
Can't call usermanager.updateUser - 'primary key conflict message' RRS feed

  • Question

  • User297437924 posted

    In the process of registering a new user with Identity, I first created the user with 'new', then I changed a field, then I called 'updateuser'.   

    Why didn't this work?   

    Dim user As ApplicationUser = New ApplicationUser() With {.UserName = UserName.Text, .ApplicationId = currentApplicationId,
    .firstname = Firstname.Text, .lastname = lastname.Text, .Email = encryptedEmail}
    user.watcherid = watcherid
    updateuser(user)
    Public Overloads Shared Sub updateuser(ByRef user As ApplicationUser)
    UserManager.Update(user)
    End Sub

    I got this error:

    System.InvalidOperationException occurred
    HResult=0x80131509
    Message=Attaching an entity of type 'ApplicationUser' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

     

    Thursday, March 8, 2018 9:39 PM

Answers

  • User297437924 posted

    I found the answer.   I have a routine that uses a copy of usermanager to create a user.  That routine then calls another routine, which creates a copy of usermanager to update the user.   So the first usermanager is still in memory while the second one is trying to update the user.   The conflict is what causes the error.   You can only have one usermanager at a time that affects a user.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Saturday, March 10, 2018 12:44 PM

All replies

  • User475983607 posted

    There is not enough code to make sense of what you're doing.   Can you explain the design?

    Thursday, March 8, 2018 10:00 PM
  • User1049247713 posted

    Not Use Id and Calling after Controller :

            private ApplicationSignInManager _signInManager;
            private ApplicationUserManager _userManager;
    
            public AccountController()
            {
            }
    
            public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager )
            {
                UserManager = userManager;
                SignInManager = signInManager;
            }
    
            public ApplicationSignInManager SignInManager
            {
                get
                {
                    return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
                }
                private set 
                { 
                    _signInManager = value; 
                }
            }
    
            public ApplicationUserManager UserManager
            {
                get
                {
                    return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
                }
                private set
                {
                    _userManager = value;
                }
            }

    and classe Edit

     public async Task<ActionResult> Edit(string id)
            {
                var user = await ApplicationUserManager.FindByIdAsync(id);
                return View(new UserViewModel(user));
            }
    
            [HttpPost]
            public async Task<ActionResult>Edit(UserViewModel model)
            {
                var user = new ApplicationUser() { Id = model.Id, Name = model.Name };
                await ApplicationUserManager.UpdateAsync(user);
                return RedirectToAction("Index");
            }

    Thursday, March 8, 2018 10:07 PM
  • User297437924 posted

    To mgebhard: I'm creating a user, then updating it.   For example, someone registers in the registration form, and gives a username, a password, and an email address.    So I create a user with that information (using 'new').   I'm not sure if 'new' means that the user is now saved to the aspnetusers table, I hope so.

    Then, in code that I left out in the example, I insert a record in another table called 'watchers', and obtain the primary key "watcherid".   I now want to update the user with 'watcherid' (I've extended the 'applicationUser' class to have a 'watcherid int' field)

    So that's why I have 'user.watcherid = watcherid"

    and that's why I then want to 'updateuser', which I hope will save the changes to the database table (with the new value of 'watcherid').

    It doesn't work, I get that message about the primary key already existing.

    To: ahmed1919:  - the code you supplied is new to me.   Are you saying I need to create an accountController class?   And a signInManager class?  The line: 

    _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();

    is puzzling - what are the question marks?   What does GetOwinContext do?

    also: you have

      await ApplicationUserManager.UpdateAsync(user);

    Is there a reason to do this with 'await' and 'UpdateAsync' rather than the way I did it?

    Much thanks to you both.

    Friday, March 9, 2018 11:19 AM
  • User475983607 posted

    To mgebhard: I'm creating a user, then updating it.   For example, someone registers in the registration form, and gives a username, a password, and an email address.    So I create a user with that information (using 'new').   I'm not sure if 'new' means that the user is now saved to the aspnetusers table, I hope so.

    No, the new key work creates a new instance of the ApplicationUser.  It does not add the ApplicationUser to the database.

    Then, in code that I left out in the example, I insert a record in another table called 'watchers', and obtain the primary key "watcherid".   I now want to update the user with 'watcherid' (I've extended the 'applicationUser' class to have a 'watcherid int' field)

    It is very difficult to understand the context when code is excluded.  Most likely you  saved the ApplicationUser when you created the WatcherId record.  Maybe a better design is first save the ApplicationUser with the UserManager.  Then Insert the UserID into the watcher record.  IMHO, you approach is not normalized property but the design is not clear.

    For example, this block has what's called a code smell.  Update is already an extension method of the UserManager type but UserManager is an instance.  That means you have a Shared UserManager.  This can/will cause concurrency issues depending on how this is coded.

    Public Overloads Shared Sub updateuser(ByRef user As ApplicationUser)
       UserManager.Update(user)
    End Sub

    To: ahmed1919:  - the code you supplied is new to me.   Are you saying I need to create an accountController class?   And a signInManager class?  The line: 

    _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();

    is puzzling - what are the question marks?   What does GetOwinContext do?

    The ?? is the null coalescing operator.

    https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operator

    also: you have

      await ApplicationUserManager.UpdateAsync(user);

    Is there a reason to do this with 'await' and 'UpdateAsync' rather than the way I did it?

    Async/await is a pattern for creating extensible code.  You can learn about async/await in the docs.

    https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

    Friday, March 9, 2018 12:38 PM
  • User297437924 posted

    Mgebhard - I read your post, and I've posted all relevant code here:

    Imports Microsoft.AspNet.Identity.EntityFramework
    Imports Microsoft.VisualBasic
    
    Public Class ApplicationDbContext
        Inherits IdentityDbContext(Of ApplicationUser)
        Public Sub New()
            MyBase.New("shmoozConnectionString") ' "DefaultConnection")
        End Sub
    End Class
    
    Public Class ApplicationUserManager
        Inherits UserManager(Of ApplicationUser)
    
        Public Sub New()
            MyBase.New(New UserStore(Of ApplicationUser)(New ApplicationDbContext()))
        End Sub
    End Class
    
    Public Class ApplicationUser
        Inherits IdentityUser
    
        Public Property ApplicationId As System.Guid
    
        Public Property MobileAlias As String
    
        Public Property IsAnonymous As Boolean
    
        Public Property LastActivityDate As System.DateTime
    
        Public Property MobilePIN As String
    
        Public Property PasswordQuestion As String
    
        Public Property PasswordAnswer As String
    
        Public Property IsApproved As Boolean
    
        Public Property IsLockedOut As Boolean
    
        Public Property CreateDate As System.DateTime
    
        Public Property LastLoginDate As System.DateTime
    
        Public Property LastPasswordChangedDate As System.DateTime
    
        Public Property LastLockoutDate As System.DateTime
    
        Public Property FailedPasswordAttemptCount As Integer
    
        Public Property FailedPasswordAttemptWindowStart As System.DateTime
    
        Public Property FailedPasswordAnswerAttemptCount As Integer
    
        Public Property FailedPasswordAnswerAttemptWindowStart As System.DateTime
    
        Public Property Comment As String
    
        Public Property watcherid As Integer
    
        Public Property posterid As Integer
    
        Public Property adminid As Integer
    
        Public Property firstname As String
    
        Public Property lastname As String
    
        Public Sub New()
            MyBase.New()
            CreateDate = DateTime.Now
            IsApproved = False
            LastLoginDate = DateTime.Now
            LastPasswordChangedDate = DateTime.Now
            LastLockoutDate = DateTime.Parse("1/1/1754")
            FailedPasswordAnswerAttemptWindowStart = DateTime.Parse("1/1/1754")
            FailedPasswordAttemptWindowStart = DateTime.Parse("1/1/1754")
    
        End Sub
    End Class

    When the user registers himself, he calls this routine:

        Protected Sub CreateUser_Click(sender As Object, e As EventArgs)
            Dim currentApplicationId As Guid
            Dim sPassword As String
            Dim sRegularEmail As String
            Dim encryptedEmail As String
            Dim result As IdentityResult
            Dim shouldRedirect As Boolean = False
            Dim sErrorMessage As String = ""
            Dim UserID As String = ""
            currentApplicationId = ClassStrings.GetApplicationID() ' article said put this code line in!
            Dim manager = New ApplicationUserManager()
            sRegularEmail = Email.Text.Trim
            encryptedEmail = ClassStrings.EncryptString(sRegularEmail, True)
    
            Dim user As ApplicationUser = New ApplicationUser() With {.UserName = UserName.Text, .ApplicationId = currentApplicationId,
                .firstname = Firstname.Text, .lastname = lastname.Text, .Email = encryptedEmail}
            sPassword = Password.Text.Trim
    
            result = manager.Create(user, sPassword)
    
            If result.Succeeded Then
             ... code to get watcherid
              
               updateUser(user)
        End Sub
    

    The routine 'updateuser' is in a class I created to make it easy to port the membership calls.   It does have a usermanager that is global to the class - but I don't think it would cause concurrency issues unless I was doing async calls.  Here is the entire class I wrote, that includes 'updateuser'.

    Imports Microsoft.Owin.Security
    
    Imports System.Collections.Generic
    Imports System.Globalization
    Imports System.Linq
    Imports System.Web
    Imports Microsoft.AspNet.Identity
    Imports System
    Imports Microsoft.VisualBasic
    Imports Microsoft.AspNet.Identity.EntityFramework
    
    Public Class TravisIOroutines
        Private Shared RoleManager As New ApplicationRoleManager
        Private Shared UserManager As New ApplicationUserManager()
    
    
        Public Shared Function GetAllUsers() As List(Of ApplicationUser)
            Dim UsersContext As New ApplicationDbContext()
            Return UsersContext.Users.ToList()
        End Function
    Public Shared Function DeleteUser(ByVal username As String) As Boolean Dim ir As IdentityResult Dim user = GetUserByName(username) ir = UserManager.Delete(user) If ir.Succeeded Then Return True Else Return False End If End Function Public Shared Function CreateUser(ByVal username As String, ByVal email As String, ByVal password As String, ByVal firstname As String, ByVal lastname As String, ByVal logintoo As Boolean, ByRef errmsg As String, ByVal postertoo As Boolean, ByVal admintoo As Boolean, ByVal shouldRedirect As Boolean, ByRef AdminSettingsStruct As ClassWebServiceShared.adminsettingstype, ByRef UserID As String, ByVal ApproveRightNow As Boolean) As Boolean Dim PaidUntilLimitViewers As Integer = 0 Dim PaidUntilLimitVideos As Integer = 0 UserID = "" If AdminSettingsStruct.OwnersPayWebsiteToGetViewers Then PaidUntilLimitVideos = 0 PaidUntilLimitViewers = 0 End If Dim user = New ApplicationUser() shouldRedirect = False errmsg = "" user.UserName = username user.Email = email user.EmailConfirmed = True user.LockoutEnabled = True 'user.PasswordQuestion 'user.PasswordAnswer user.IsApproved = ApproveRightNow user.CreateDate = Now() user.Comment = "" user.firstname = firstname user.lastname = lastname Dim IR As IdentityResult IR = UserManager.Create(user, password) If IR.Succeeded Then UserManager.AddToRole(user.Id, "zUser") ClassStrings.insertWatcher(user.Id, firstname, lastname, user.watcherid, False) ' above sets user.watcherid byref. If logintoo Then If Not Login(user.UserName, password, shouldRedirect, errmsg, UserID) Then errmsg = "Error: unable to login, even though the user (" + username + ") was created." Return False End If End If Return True ' this is for security questions, if we put them in 'Dim userExts = New AspNetUsersExt() 'userExts.UserId = user.Id 'userExts.SecurityQuestion = securityQuestion 'userExts.SecurityAnswerSalt = salt 'userExts.SecurityAnswer = securityAnswer 'Using entities = New MyEntities() ' entities.AspNetUsersExts.Add(userExts) ' entities.SaveChanges() 'End Using Else errmsg = "Error: user (" + username + ") could not be created." Return False End If End Function Public Shared Function Login(ByVal username As String, ByVal password As String, ByRef shouldRedirect As Boolean, ByRef errMsg As String, ByRef UserID As String) As Boolean shouldRedirect = False errMsg = "" UserID = "" Dim user As ApplicationUser = UserManager.FindByName(username) If user Is Nothing Then errMsg = "Error: cannot find a user with username of " + username Return False End If UserID = user.Id If UserManager.IsLockedOut(user.Id) Then errMsg = "Error: this user is locked out" Return False End If Dim sph As New SQLPasswordHasher Dim result As PasswordVerificationResult ' = UserManager.PasswordHasher.VerifyHashedPassword(user.PasswordHash, password) result = sph.VerifyHashedPassword(user.PasswordHash, password) If result = PasswordVerificationResult.Success Then UserAuthenticated(UserManager, user) shouldRedirect = True ElseIf result = PasswordVerificationResult.SuccessRehashNeeded Then user.PasswordHash = sph.HashPassword(password) UserAuthenticated(UserManager, user) shouldRedirect = True Else user.AccessFailedCount += 1 If user.AccessFailedCount >= 10 Then user.LockoutEndDateUtc = DateTime.UtcNow.AddMinutes(15) UserManager.Update(user) errMsg = "Error: password is incorrect" Return False End If Return True End Function Public Shared Sub Logout() Dim authenticationManager = HttpContext.Current.GetOwinContext().Authentication authenticationManager.SignOut() End Sub Public Shared Sub UserAuthenticated(ByVal userManager As ApplicationUserManager, ByVal user As ApplicationUser) Dim authenticationManager = HttpContext.Current.GetOwinContext().Authentication Dim userIdentity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie) authenticationManager.SignIn(New AuthenticationProperties() With {.IsPersistent = False}, userIdentity) user.AccessFailedCount = 0 user.LockoutEndDateUtc = Nothing userManager.Update(user) End Sub Public Shared Function validateUser(ByVal username As String, ByVal password As String) As Boolean Dim thisUser As ApplicationUser thisUser = UserManager.Find(username, password) If thisUser Is Nothing Then Return False End If Return True End Function Public Overloads Shared Function GetUserRoles(ByVal username As String) As List(Of String) Dim user As ApplicationUser = UserManager.FindByName(username) Dim rolenames As List(Of String) rolenames = UserManager.GetRoles(user.Id) Return rolenames End Function Public Shared Function GetUserByEmail(ByVal Email As String) As ApplicationUser Dim user As ApplicationUser = Nothing If Email Is Nothing Then Return Nothing End If If Email.Trim.Length = 0 Then Return Nothing End If user = UserManager.FindByEmail(Email) Return user End Function Public Shared Function GetUsernameFromEmail(ByVal strEmail As String) As String Dim user As ApplicationUser = GetUserByEmail(strEmail) If user Is Nothing Then Return "" End If Return user.UserName End Function Public Shared Function GetUserById(ByVal userid As String) As ApplicationUser Dim user As ApplicationUser = Nothing If userid Is Nothing Then Return Nothing End If If userid.Trim.Length = 0 Then Return Nothing End If user = UserManager.FindById(userid) Return user End Function Public Shared Function GetUsernameById(ByVal userid As String) As String Dim user As ApplicationUser = Nothing If userid Is Nothing Then Return String.Empty End If If userid.Trim.Length = 0 Then Return String.Empty End If user = UserManager.FindById(userid) Return user.UserName End Function Public Shared Function GetUserByName(ByVal username As String) As ApplicationUser Dim user As ApplicationUser = Nothing If username Is Nothing Then Return Nothing End If If username.Trim.Length = 0 Then Return Nothing End If user = UserManager.FindByName(username) Return user End Function Public Shared Sub AddUserToRole(ByVal userName As String, ByVal roleName As String) Try Dim user = UserManager.FindByName(userName) UserManager.AddToRole(user.Id, roleName) UserManager.Update(user) ' used to have context.savechanges!!!!???? SAD!!! Catch Throw End Try End Sub Public Shared Sub RemoveUserFromRole(ByVal userName As String, ByVal roleName As String) Try Dim user = UserManager.FindByName(userName) UserManager.RemoveFromRole(user.Id, roleName) UserManager.Update(user) ' used to have context.savechanges!!!!???? SAD!!! Catch Throw End Try End Sub
    ' HERE IT IS Public Overloads Shared Sub updateuser(ByRef user As ApplicationUser) UserManager.Update(user) End Sub Public Shared Function IsUserInRole(ByVal username As String, ByVal rolename As String) As Boolean Dim user = UserManager.FindByName(username) ' he did a ctype of this to 'student'! Return (UserManager.IsInRole(user.Id, rolename)) End Function Public Shared Function CreateRole(rolename As String) As Boolean Dim roleResult As IdentityResult If Not RoleManager.RoleExists(roleName) Then roleResult = RoleManager.Create(New IdentityRole(roleName)) End If Return True End Function End Class

    So does it make sense that 'updateuser' should fail?

    Friday, March 9, 2018 8:56 PM
  • User297437924 posted

    I found the answer.   I have a routine that uses a copy of usermanager to create a user.  That routine then calls another routine, which creates a copy of usermanager to update the user.   So the first usermanager is still in memory while the second one is trying to update the user.   The conflict is what causes the error.   You can only have one usermanager at a time that affects a user.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Saturday, March 10, 2018 12:44 PM