我正在使用AspNetCore.Identity
来管理在我的应用程序中注册的用户。我有两个桌子:
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
异常。
答案 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
。