如何正确扩展 IdentityUser 以保存每个用户的集合

How to correctly extend IdentityUser to save collections for each user

我希望用户能够 select 从 MultiSelectList 下拉列表中选择多项技能。我能够将每个用户的多种技能 selections 保存到数据库中,但我发现如果我删除一个用户的技能选项,并且我使用以前保存过相同技能的不同用户登录他的 selections,该用户和所有其他拥有类似技能选项的用户都会被删除。

所以我很清楚。假设用户 A 保存了这些技能 ["C#", "Python", "Java"]。用户B当前当前保存的技能为["C++","Scala"]。然后用户 B 登录并决定添加他刚刚学习的 C#。一旦他更新了个人资料,他的 selection 就变成了这个 ["C++","Scala", "C#"]。 C# 将从用户 A 的 selections 中删除,因此它变为 ["Python", "Java"].

这是我的习惯IdentityUserclass.

public class ApplicationUser : IdentityUser
{
    public ApplicationUser()
    {
        Skills = new List<Skill>();
    }
    public string Location { get; set; }
    public virtual ICollection<Skill> Skills { get; set; }
}

这是技能模型。

public class Skill
{
    public int SkillId { get; set; }
    public string SkillType { get; set; }
}

这就是我在控制器中保存技能 selection 的方式。

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Profile(ProfileViewModel profileModel)
{
    var user = await _userManager.GetUserAsync(User);

    if (user == null)
    {
        return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
    }
    if (ModelState.IsValid)
    {
        if (user.Location != profileModel.Location) user.Location = profileModel.Location;

        if (profileModel.SelectedSkillIds != null)
        {
            List<Skill> tempSkills = new List<Skill> { };
            foreach (var skillID in profileModel.SelectedSkillIds)
            {
                user.Skills.Add(_context.Skills.FirstOrDefault(x => x.SkillId == skillID));
                var skill = _context.Skills.Find(skillID);
                if (skill != null)
                {
                    user.Skills.Add(skill);
                    tempSkills.Add(skill);
                }
                var allSkills = _context.Skills.ToList();
                var skillsToRemove = allSkills.Except(tempSkills);
                foreach (var sk in skillsToRemove)
                {
                    user.Skills.Remove(sk);
                }
            }
            await _userManager.UpdateAsync(user);
            await _signInManager.RefreshSignInAsync(user);
            return RedirectToAction("Profile", "Account");
        }
        return View(profileModel);
    }
}

更新 - 我如何删除 selections

if (profileModel.SelectedSkillIds != null)
{
    List<UserSkill> tempSkills = new List<UserSkill> { };
    foreach (var skillID in profileModel.SelectedSkillIds)
    {
        var skill = _context.Skills.Find(skillID);
        if (skill != null)
        {
            var userskill = new UserSkill { AppUserId = user.Id, SkillId = skill.SkillId };
            user.UserSkills.Add(userskill);
            tempSkills.Add(userskill);
        }
    var allSkills = _context.UserSkills.ToList();
    var skillsToRemove = allSkills.Except(tempSkills);
    foreach (var sk in skillsToRemove)
    {
        user.UserSkills.Remove(sk); 
    }
}

你应该创建一个像 UserSkills 一样的 class,它有 UserIdSkillId,在这种情况下,任何用户都可以拥有多种技能,并且任何技能都可以用于许多人用户。见 many-to-many, 1,2

你应该把你的模型改成这个

public class ApplicationUser : IdentityUser
{
    public ApplicationUser()
    {
        Skills = new List<Skill>();
    }
    public string Location { get; set; }
    public virtual ICollection<UserSkills> Skills { get; set; }
}
public class Skill
{
    public int SkillId { get; set; }
    public string SkillType { get; set; }
    public virtual ICollection<UserSkills> Skills { get; set; }
}
public class UserSkills
{
    public int Id { get; set }
    public int UserId { get; set }
    public int SkillId { get; set }
    public Skill Skill { get; set; }
    public ApplicationUser User { get; set; }
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Profile(ProfileViewModel profileModel)
{
    var user = await _userManager.GetUserAsync(User);

    if (user == null)
    {
        return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
    }
    if (ModelState.IsValid)
    {
        if (user.Location != profileModel.Location) user.Location = profileModel.Location;

        if (profileModel.SelectedSkillIds != null)
        {
            List<Skill> tempSkills = new List<Skill> { };
            foreach (var sk in user.UserSkills)
            {
               user.UserSkills.Remove(sk);
            }
            foreach (var skillID in profileModel.SelectedSkillIds)
            {
                var userSkills = new UserSkill { UserId = user.Id, SkillId = skillID };
                user.UserSkills.Add(userSkills);
            }
            await _userManager.UpdateAsync(user);
            await _signInManager.RefreshSignInAsync(user);
            return RedirectToAction("Profile", "Account");
        }
        return View(profileModel);
    }
}

然后将 UserSkills 添加到您的 DbContext

public DbSet<Skill> Skills { get; set; }
public DbSet<UserSkill> UserSkills { get; set; }

最终在package manager console

中使用Add-MigrationUpdate-DataBase

其他选项

您可以在控制器中注入 DbContext 并在 UserSkill

中添加 UserSkills 数据
private readonly YourDbContext _dbContext;
public UserController(YourDbContext dbContext)
{
    _dbContext = dbContext;
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Profile(ProfileViewModel profileModel)
{
    var user = await _userManager.GetUserAsync(User);

    if (user == null)
    {
        return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
    }
    if (ModelState.IsValid)
    {
        if (user.Location != profileModel.Location) user.Location = profileModel.Location;

        if (profileModel.SelectedSkillIds != null)
        {
            var userSkillsForDelete = _dbContext.UserSkills.Where(a => a.UserId == user.Id).ToList();//<-- NOTE THIS
            foreach (var sk in userSkillsForDelete)
            {
               //user.UserSkills.Remove(sk);
               _dbContext.UserSkills.Remove(sk);<--NOTE THIS
            }
            foreach (var skillID in profileModel.SelectedSkillIds)
            {
                var userSkills = new UserSkill { UserId = user.Id, SkillId = skillID };
               _dbContext.UserSkills.Add(userSkills);<--NOTE THIS
            }
            await _userManager.UpdateAsync(user);
            await _signInManager.RefreshSignInAsync(user);
            return RedirectToAction("Profile", "Account");
        }
        return View(profileModel);
    }
}