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

时间:2016-10-03 16:30:22

标签: c# entity-framework asp.net-core entity-framework-core

我正在尝试更新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不为空时,我无法设置新记录:< / p>

错误:

  

“无法跟踪实体类型'ProjectEmployee'的实例   因为此类型的另一个具有相同密钥的实例已经存在   被跟踪。添加新实体时,对于大多数键类型都是唯一的   如果没有设置密钥,则将创建临时密钥值(即,如果密钥   属性被指定为其类型的默认值)。如果你是   明确设置新实体的关键值,确保不这样做   与现有实体或为其他实体生成的临时值发生冲突   新实体。附加现有实体时,请确保只有一个实体   具有给定键值的实体实例附加到上下文。“

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

3 个答案:

答案 0 :(得分:2)

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 );

答案 1 :(得分:0)

我认为旧的对象并没有真正从数据库中删除。你只是调用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,只需添加它。

答案 2 :(得分: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();