首页 文章

实体框架核心 - 更新相关集合

提问于
浏览
3

我正在尝试更新ProjectModel中ProjectEmployees的集合 . 我想删除所有旧值并设置新值 .

我的模特:

public class Project
{
    ... 
    public ICollection<ProjectEmployee> ProjectEmployees { get; set; }
}

public class ProjectEmployee
{
    public int ProjectId { get; set; }
    public virtual Project Project { get; set; }
    public int UserId { get; set; }
    public virtual Employee Employee { get; set; }
}

public class Employee
{
    public int UserId { get; set; }
    public User User { get; set; }
    ...
}
public class ProjectGroupModel //ViewModel
{
    public int ProjectId { get; set; }
    public ICollection<Employee> ProjectEmployees { get; set; }
}

这是典型的多对多关系 .

我的控制器动作:

[HttpPost("group")]
    public async Task<IActionResult> CreateGroup([FromBody] ProjectGroupModel pro)
    {
            var dbProject = await _context.Project
                .Include(p=>p.ProjectEmployees)
                .FirstAsync(p => p.ProjectId == pro.ProjectId);
            dbProject.ProjectEmployees.Clear();

            foreach (var emp in pro.ProjectEmployees)
            {
                dbProject.ProjectEmployees.Add(new ProjectEmployee()
                {
                    UserId = emp.UserId
                });
            }

            await _context.SaveChangesAsync();

            return Ok();
    }

当pro.ProjectEmployees为空时,dbProject.ProjectEmployees中的所有记录都被正确删除,如果dbProject.ProjectEmployees为空,则添加了来自model的新记录,但是当dbProject.ProjectEmployees不为空时,我无法设置新记录:

错误:

“无法跟踪实体类型'ProjectEmployee'的实例,因为已经跟踪了具有相同键的此类型的另一个实例 . 添加新实体时,对于大多数键类型,如果未设置任何键,将创建唯一的临时键值(即,如果为键属性指定了其类型的默认值) . 如果要为新实体显式设置键值,请确保它们不会与现有实体或为其他新实体生成的临时值发生冲突 . 附加现有实体时,请确保只有一个具有给定键值的实体实例附加到上下文 . “

我尝试以数百种方式修复此操作,但总是错误的 .

3 回答

  • 1

    我认为旧的对象并没有真正从数据库中删除 . 你只是调用Clear()这还不够 . 试着这样做:

    [HttpPost("group")]
    public async Task<IActionResult> CreateGroup([FromBody] ProjectGroupModel pro)
    {
        var dbProject = await _context.Project
            .Include(p=>p.ProjectEmployees)
            .FirstAsync(p => p.ProjectId == pro.ProjectId);
    
        foreach (var old in dbProject.ProjectEmployees)
            {
                _context.ProjectEmployee.Remove(old);
            }
    
        dbProject.ProjectEmployees.Clear();
    
        foreach (var emp in pro.ProjectEmployees)
            {
                dbProject.ProjectEmployees.Add(new ProjectEmployee()
                {
                    UserId = emp.UserId
                });
            }
    
        await _context.SaveChangesAsync();
    
        return Ok();
    }
    

    在这里,我假设您在上下文中有适当的设置(_context.ProjectEmployee) . 如果您在上下文中没有明确地使用此Set,则只需添加它 .

  • 0

    它远非完美,但我能够使其工作的唯一方法是从相应的 DbSet 中删除项目并在添加新项目之前调用 SaveChanges

    var dbProject = await _context.Project
        .Include(p=>p.ProjectEmployees)
        .FirstAsync(p => p.ProjectId == pro.ProjectId);
    
    if (dbProject.ProjectEmployees.Any())
    {
        _context.ProjectEmployee.RemoveRange(dbProject.ProjectEmployees);
        await _context.SaveChangesAsync();
    }
    
    foreach (var emp in pro.ProjectEmployees)
    {
        dbProject.ProjectEmployees.Add(new ProjectEmployee()
        {
            UserId = emp.UserId
        });
    }
    
    await _context.SaveChangesAsync();
    
  • 0

    链接到another SO question

    我可以在这里和你的课程一起回答 .

    然后使用此扩展我删除未选中并将新选择添加到列表中

    public static void TryUpdateManyToMany<T, TKey>(this DbContext db, IEnumerable<T> currentItems, IEnumerable<T> newItems, Func<T, TKey> getKey) where T : class
        {
            db.Set<T>().RemoveRange(currentItems.Except(newItems, getKey));
            db.Set<T>().AddRange(newItems.Except(currentItems, getKey));
        }
    
        public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKeyFunc)
        {
            return items
                .GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems })
                .SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp })
                .Where(t => ReferenceEquals(null, t.temp) || t.temp.Equals(default(T)))
                .Select(t => t.t.item);
        }
    

    使用它看起来像这样

    var model = db.Employees
                 .Include(x => x.ProjectEmployees)
                 .FirstOrDefault(x => x.EmployeeId == employee.EmployeeId);
    
     db.TryUpdateManyToMany(model.ProjectEmployees, listOfNewProjectIds
     .Select(x => new ProjectEmployee
     {
         ProjectId = x,
         EmployeeId = employee.EmployeeId
     }), x => x.ProjectId );
    

相关问题