无法更新用户

时间:2018-10-16 09:19:41

标签: asp.net asp.net-core

我正在使用AspNetCore.Identity来管理在我的应用程序中注册的用户。我有两个桌子:

  • AspNetUsers:默认创建
  • UserDetails:新表,其中包含每个注册用户的额外信息

UserDetails表包含一个FK,它指向UserId中的AspNetUsers。这些模型如下:

public class User : IdentityUser
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
    public string LockoutMessage { get; set; }
    public string SessionId { get; set; }

    public virtual UserDetails UserDetail { get; set; }
}

public class UserDetails
{
    public string UserId { get; set; }
    public string Biography { get; set; }
    public string Country { get; set; }
    public string FacebookLink { get; set; }
    public string TwitterLink { get; set; }
    public string SkypeLink { get; set; }
    public string Address { get; set; }
    public string State { get; set; }
    public string Gender { get; set; }

    public User User { get; set; }
}

为了使用这些新字段更新数据库,我这样做:

add-migration MigrationName -context AppContext
update-database

,并且有效。现在,我尝试更新当前登录的用户,因此我在UserRepository类内创建了一个方法,该方法包含以下内容:

public UserProfileViewModel UpdateUserAsync(UserProfileViewModel updatedUser)
{
    var originalUser = _db.Users.FirstOrDefault(c => c.UserName == updatedUser.Username);

    originalUser.FirstName = updatedUser.FirstName;
    originalUser.UserDetail = new UserDetails();
    originalUser.UserDetail.Biography = updatedUser.Biography;

    _db.SaveChanges();
    return updatedUser;
}

基本上,我将包含要更新的属性的表单的ViewModel发送到此方法,然后在应用程序上下文实例_db中搜索用户并更新了所有字段。

当代码到达_db.SaveChanges()时,问题就来了,实际上我得到了这个错误:

  

SqlException:违反PRIMARY KEY约束“ PK_UserDetails”。无法在对象“ dbo.UserDetails”中插入重复键。

我猜代码正在尝试插入字段而不是更新,这很奇怪,有人可以帮助我吗?

更新

正如您在上面看到的,我在AspNetUsers表中添加了额外的字段,为此,我从IdentityUser类继承了User,该类曾在这里用来定义实体: >

services.AddIdentity<User, IdentityRole>()
        .AddEntityFrameworkStores<AppContext>()
        .AddDefaultTokenProviders();

奇怪的是,当我添加新记录时,UserDetails表已正确填充,但是当我需要更新时,我猜测UserDetail为null,而与此同时我无法添加另一个UserDetail,因为已经存在,并且会生成CONSTRAINT异常。

1 个答案:

答案 0 :(得分:1)

Entity Framework Core不会加载相关数据,除非您指示使用几个选项之一进行加载,所有这些选项均已记录在Loading Related Data中。针对DbSet运行以下查询时:

_db.Users
    .FirstOrDefault(c => c.UserName == updatedUser.Username);

未填充UserDetail导航属性的值。为了解决这个问题,您可以使用以下修改:

_db.Users
    .Include(u => u.UserDetail)
    .FirstOrDefault(c => c.UserName == updatedUser.Username);

正如您在OP本身的注释中所指出的那样,这引入了一个新的错误,因为将此方法与_userManager.UpdateUserAsync结合使用会抱怨User已经被跟踪。同样在评论中,您提出了以下问题:

  

目前我固定为:await _db.SaveChangesAsync();所以我想我需要使用数据库上下文实例而不是usermanager,不幸的是,某些密码哈希之类的方法仅在用户管理器中可用,不能仅使用db实例吗?

为了在此处获得理想的结果,您应该可以使用类似以下的内容:

public async Task<UserProfileViewModel> UpdateUserAsync(UserProfileViewModel updatedUser)
{
    var originalUser = await _userManager.FindByNameAsync(updatedUser.Username);

    // This is the "magic" (ok, it's a bit of a hack).
    _db.Entry(originalUser).Reference(u => u.UserDetail).Load();

    ...

    await _userManager.UpdateUserAsync(originalUser);
    return updatedUser;
}

在GitHub问题here中提出了这种特定方法,它实际上只是加载相关数据的另一种方式(如我提供的链接中所述)。

注意:为了将UpdateUserAsync函数使用,我已经将您的async/await函数转换为使用UserManager