I'm trying to update a user with a user repository service. I'm still using the IdentityUser
, just modified it to use int
as primary key instead of string/guid.
Everything works fine, until my Update
throws an exception.
So this is my update method in my BaseRepository
class:
public virtual void Update(T entity)
{
EntityEntry dbEntityEntry = _context.Entry<T>(entity);
dbEntityEntry.State = EntityState.Modified;
}
public virtual void Commit()
{
_context.SaveChanges();
}
The call itself goes:
[HttpPost]
[Route("home/account/edit")]
public ActionResult Edit(User user)
{
if (!ModelState.IsValid)
return View(user);
string id = _userManager.GetUserId(User);
user.Id = id.To<int>();
_userService.Update(user);
_userService.Commit();
return RedirectToAction("Index");
}
(nothing special I think)
So now, when it's in Commit
to save my changes, it throws an exception:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
I thought, okay, I check the profiler, couldn't be that tricky. Profiler shows me the statement, entity framework sent him:
exec sp_executesql N'SET NOCOUNT ON;
UPDATE [AspNetUsers] SET [AccessFailedCount] = @p0, [Birthday] = @p1, [ConcurrencyStamp] = @p2, [Email] = @p3, [EmailConfirmed] = @p4, [FirstName] = @p5, [Language] = @p6, [LastName] = @p7, [LockoutEnabled] = @p8, [LockoutEnd] = @p9, [MiddleName] = @p10, [NormalizedEmail] = @p11, [NormalizedUserName] = @p12, [PasswordHash] = @p13, [PhoneNumber] = @p14, [PhoneNumberConfirmed] = @p15, [SecurityStamp] = @p16, [TwoFactorEnabled] = @p17, [UserName] = @p18, [VerificationToken] = @p19
WHERE [Id] = @p20 AND [ConcurrencyStamp] = @p21;
SELECT @@ROWCOUNT;
',N'@p20 int,@p0 int,@p1 datetime2(7),@p2 nvarchar(4000),@p21 nvarchar(4000),@p3 nvarchar(256),@p4 bit,@p5 nvarchar(4000),@p6 nvarchar(4000),@p7 nvarchar(4000),@p8 bit,@p9 nvarchar(4000),@p10 nvarchar(4000),@p11 nvarchar(256),@p12 nvarchar(256),@p13 nvarchar(4000),@p14 nvarchar(4000),@p15 bit,@p16 nvarchar(4000),@p17 bit,@p18 nvarchar(256),@p19 nvarchar(4000)',@p20=1,@p0=0,@p1='1991-06-10 00:00:00',@p2=N'24fe11c5-a831-45ce-8960-16aa78b280df',@p21=N'24fe11c5-a831-45ce-8960-16aa78b280df',@p3=N'my@email.ch',@p4=0,@p5=N'Matthias',@p6=N'de-DE',@p7=N'Burger',@p8=0,@p9=NULL,@p10=NULL,@p11=NULL,@p12=NULL,@p13=NULL,@p14=NULL,@p15=0,@p16=NULL,@p17=0,@p18=NULL,@p19=NULL;
So, [ConcurrencyStamp] = @p21
and @p21=N'24fe11c5-a831-45ce-8960-16aa78b280df'
Checked my record of that user...
ConcurrencyStamp
is 22fa7d03-1382-4eee-8c17-878253fdf1c4
So my questions are:
- What is that
ConcurrencyStamp
? - Why is Entity Framework filtering for it? He gets my User ID. Isn't that enough?
- Why has the user object another
ConcurrencyStamp
than the one in the database? When Entity Framework wants to filter for it, shouldn't they be the same? - Does anybody have an idea? How could/should I fix it? Or am I doing something wrong?
Update:
Model (for now viewmodel, too):
public class User : IdentityUser<int>, IEntity
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public DateTime Birthday { get; set; }
public string Language { get; set; }
public ICollection<MailBox> MailBoxes { get; set; } = new List<MailBox>();
public string VerificationToken { get; set; }
}