【问题标题】:Entity Framework Many-to-Many Inserting Fine, But Not Properly Updating实体框架多对多插入很好,但没有正确更新
【发布时间】:2015-09-30 13:56:10
【问题描述】:

早上好!

我在 Employee 和 Skill 实体之间有一个多对多的关系。当我创建一个新员工时,我选择的技能会毫无问题地添加到数据库中。但是,当我更新员工时,员工内容会更新,但不会添加/删除任何技能。我看到它们被传递到存储库,但它没有更新数据库。

我有以下多对多关系:

public class Employee : BaseEntity
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string JobTitle { get; set; }

    public virtual ICollection<Skill> Skills { get; set; }
}

和:

public class Skill : BaseEntity
{
    public string Name { get; set; }

    public virtual ICollection<Employee> Employees { get; set; }
}

我的控制器通过以下方法添加/删除员工的技能:

public ActionResult Edit(int id, EmployeeEditViewModel viewModel)
{
    try
    {
        if (!ModelState.IsValid)
        {
            viewModel.SkillsList = _skillService.GetAll().ToList();
            return View(viewModel);
        }

        var employee = Mapper.Map<Employee>(viewModel);

        UpdateSkills(employee, viewModel.NewSkills);

        _employeeService.Update(employee);

        return RedirectToAction("Index");
    }
    catch(Exception e)
    {
        ModelState.AddModelError("", e.Message);
        viewModel.SkillsList = _skillService.GetAll().ToList();
        return View(viewModel);
    }
}



    private void UpdateSkills(Employee employee, IEnumerable<int> updatedSkills)
    {
        if (employee.Skills != null)
        {
            var updatedSkillsList = updatedSkills as IList<int> ?? updatedSkills.ToList();
            var addedSkills = updatedSkillsList.Except(employee.Skills.Select(x => x.Id));
            var removedSkills = employee.Skills.Select(x => x.Id).Except(updatedSkillsList);

            addedSkills.ForEach(x => employee.Skills.Add(_skillService.GetById(x)));
            removedSkills.ForEach(x => employee.Skills.Remove(_skillService.GetById(x)));
        }
        else
        {
            employee.Skills = new List<Skill>();
            newSkills.ForEach(x => employee.Skills.Add(_skillService.GetById(x)));
        }
    }

然后使用通用存储库插入/更新员工:

public void Insert(TEntity entity)
{
    if (entity == null)
        throw new ArgumentNullException("entity");

    try
    {
        _dbSet.Add(entity);
        _dbContext.SaveChanges();
    }
    catch (DbEntityValidationException ex)
    {
        ThrowValidationError(ex);
    }
}


    public void Update(TEntity entity)
    {
        if (entity == null)
            throw new ArgumentNullException("entity");

        try
        {
            _dbSet.Attach(entity);
            _dbContext.Entry(entity).State = EntityState.Modified;
            _dbContext.SaveChanges();
        }
        catch (DbEntityValidationException ex)
        {
            ThrowValidationError(ex);
        }
    }

这是从数据上下文中调用 Employee 对象的方式。

我的构造函数:

protected readonly NTierApplicationsDataContext _dbContext;
        protected readonly DbSet<TEntity> _dbSet;

        public EfRepository(NTierApplicationsDataContext dbContext)
        {
            _dbContext = dbContext;
            _dbSet = _dbContext.Set<TEntity>();
        } 

这里是获取对象的find方法:

public TEntity GetById(int id)
        {
            return _dbSet.Find(id);
        }

【问题讨论】:

  • 你如何获得`employee'对象?你从上下文中得到它吗?为了使功能正常工作,EF 必须激活对象跟踪。
  • 是的,在我的通用存储库中,我从应用程序数据上下文中调用它。
  • 添加了上面的代码。

标签: c# asp.net entity-framework


【解决方案1】:

我认为您必须在编辑以下技能后自行更新员工

private void UpdateSkills(Employee employee, IEnumerable<int> updatedSkills)
{
    if (employee.Skills != null)
    {
        var updatedSkillsList = updatedSkills as IList<int> ?? updatedSkills.ToList();
        var addedSkills = updatedSkillsList.Except(employee.Skills.Select(x => x.Id));
        var removedSkills = employee.Skills.Select(x => x.Id).Except(updatedSkillsList);

        addedSkills.ForEach(x => employee.Skills.Add(_skillService.GetById(x)));
        removedSkills.ForEach(x => employee.Skills.Remove(_skillService.GetById(x)));

         // here 
         _employeeService.Update(employee);
    }
    else
    {
        employee.Skills = new List<Skill>();
        newSkills.ForEach(x => employee.Skills.Add(_skillService.GetById(x)));
    }
}

编辑:检查映射

编辑:

我认为你的实体的映射有问题,你可以换一种方法

public ActionResult Edit(int id, EmployeeEditViewModel viewModel)
{
   try
   {
    if (!ModelState.IsValid)
    {
        viewModel.SkillsList = _skillService.GetAll().ToList();
        return View(viewModel);
    }

    //here your mapper is not attaching the employee to the context
    //var employee = Mapper.Map<Employee>(viewModel);

    you can do this 
    var employee = _employeeService.GetById(viewModel.Id);
    // after that ... update what the user did from the view model except the id as the id won't change
    employee = Mapper.Map<Employee>(viewModel, employee);
    // I think that the mapping have another overload to map to a destination. you can set the setup for the mappnig in the startup to ignore updating Ids        

    UpdateSkills(viewModel.NewSkills);

    _employeeService.Update(employee);

    return RedirectToAction("Index");
}
catch(Exception e)
{
    ModelState.AddModelError("", e.Message);
    viewModel.SkillsList = _skillService.GetAll().ToList();
    return View(viewModel);
}

}

【讨论】:

  • 你是对的。这就是我正在做的事情。对不起,我没有在我的原始帖子中包含它。我已经更新了上面的代码。
  • 对不起,没有。我已经准备好了。就像我说的,它正在更新 Employee 内容,而不是多对多关系中的相关数据。我认为 Ghasan 没有为相关实体激活对象跟踪是正确的,但我不知道如何解决这个问题。
  • 我认为你可以做一件事......在更新技能中......删除用户拥有而不是更新......并从头开始添加所有内容,'UpdateSkills(Employee e,IEnumerable 技能){ e.Skills = new List(); Skills.ForEach(x=> e.Skills.Add(_skillService.GetById(x))); }'
  • 不走运。我得到了同样的结果。
猜你喜欢
  • 1970-01-01
  • 2017-02-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-02
  • 2011-05-14
  • 2012-11-27
  • 2018-08-20
  • 1970-01-01
相关资源
最近更新 更多