C# 实体框架:更新用户失败

C# 实体框架:更新用户失败,c#,entity-framework,asp.net-core,C#,Entity Framework,Asp.net Core,我正在尝试使用用户存储库服务更新用户。我仍然在使用IdentityUser,只是将其修改为使用int作为主键,而不是字符串/guid 一切正常,直到我的Update抛出异常 这是我在BaseRepository类中的更新方法: public virtual void Update(T entity) { EntityEntry dbEntityEntry = _context.Entry<T>(entity); dbEntityEntry.State = Entity

我正在尝试使用用户存储库服务更新用户。我仍然在使用
IdentityUser
,只是将其修改为使用
int
作为主键,而不是字符串/guid

一切正常,直到我的
Update
抛出异常

这是我在
BaseRepository
类中的更新方法:

public virtual void Update(T entity)
{
    EntityEntry dbEntityEntry = _context.Entry<T>(entity);
    dbEntityEntry.State = EntityState.Modified;
}

public virtual void Commit()
{
    _context.SaveChanges();
}
因此,
[ConcurrencyStamp]=@p21
@p21=N'24fe11c5-a831-45ce-8960-16aa78b280df'

检查了我的用户记录

ConcurrencyStamp
is
22fa7d03-1382-4eee-8c17-878253fdf1c4

因此,我的问题是:

  • 那是什么
    ConcurrencyStamp
  • 为什么实体框架会对其进行过滤?他得到了我的用户ID,这还不够吗
  • 为什么用户对象比数据库中的对象有另一个
    ConcurrencyStamp
    ?当实体框架想要过滤它时,它们不应该是相同的吗
  • 有人有主意吗?我该如何修理它?还是我做错了什么
更新

模型(也适用于现在的viewmodel):

public类用户:IdentityUser,IEntity
{
公共字符串名{get;set;}
公共字符串MiddleName{get;set;}
公共字符串LastName{get;set;}
公共日期时间生日{get;set;}
公共字符串语言{get;set;}
公共ICollection邮箱{get;set;}=new List();
公共字符串验证项{get;set;}
}

存在
ConcurrencyStamp
是为了防止两个人对同一用户进行两次不同的更改,而不知道对方的更改

    /// <summary>
    /// A random value that must change whenever a user is persisted to the store
    /// </summary>
    public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString();
//
///一个随机值,每当用户被持久化到存储时,该值必须更改
/// 
公共虚拟字符串ConcurrencyStamp{get;set;}=Guid.NewGuid().ToString();
原则上,这应该在创建用户时分配一次,并且(我假设)数据库中应该有一种类似触发器的机制,每当数据库中的条目更新时,该机制会导致值更改

发件人:

如果查看OnModelCreating()方法中IdentityDbContext的实现,您会发现:

builder.Entity<TUser>(b =>
{
....
    b.Property(u => u.ConcurrencyStamp).IsConcurrencyToken();
....
builder.Entity(b=>
{
....
b、 属性(u=>u.ConcurrencyStamp).IsConcurrencyToken();
....
因此,实体框架意识到这应该被视为并发令牌。它将检查属性值和ID,以确保在用户对象从数据库中加载后,如果用户已更改,则更新将失败

至于为什么值不相同,这可能是您必须通过调试来解决的问题。以下是一些您可能会检查的理论:

  • 可能用户对象在加载后确实发生了更改?如果用户对象使用其原始值加载了两次,然后单独更改了两次,则会发生这种情况
  • 也许您正在基于传入web请求的值构造用户对象,但您没有基于从数据库中提取ConcurrencyStamp令牌时的值映射ConcurrencyStamp令牌,因此它保留了首次构造时随机生成的GUID

  • 你能给我们看看你的模型吗please@SimonPrice更新:)model现在也是viewmodel。它或多或少是用于测试的。您的代码很奇怪。您使用登录用户的ID,但使用传递的用户的数据?此代码尝试始终创建具有登录用户ID的用户。而且您似乎从未附加模型。您在更新模型之前附加模型了treid吗?是的,这是i用于更新自己的配置文件数据。因此,传递的用户是登录用户。您是否将并发令牌(
    ConcurrencyStamp
    )传递回操作?它用于检查您尝试更新的模型是否已从其他操作更新。如果令牌不同,则更新失败(因为并发令牌将成为where子句的一部分)它几乎是第二个。我的视图发送了一个带有更新数据的新用户对象,我试图将其更新到数据库中。这里没有设置
    ConcurrencyStamp
    集(从原始对象映射,因为视图正在重置模型),因此生成了一个新的ConcurrencyStamp,并且可以肯定它们不匹配。
    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; }
    }
    
        /// <summary>
        /// A random value that must change whenever a user is persisted to the store
        /// </summary>
        public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString();
    
    builder.Entity<TUser>(b =>
    {
    ....
        b.Property(u => u.ConcurrencyStamp).IsConcurrencyToken();
    ....