2016-10-03 69 views
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是从模型空的新记录加入,但是当dbProject.ProjectEmployees是不是空的,我不能创下新的记录:

错误:

"The instance of entity type 'ProjectEmployee' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context."

我试图修复在数百这个动作但总是这样做是错误的。

+0

应该转换dbProject使用ToList方法列出使用这个扩展我做了,除去未选中并添加新的选择列表

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

。然后尝试更新它 –

+0

在我看来,像主要关键冲突。当您创建新的ProjectEmployee()时,您只能设置UserId。你不应该也设置ProjectId? –

+0

“ProjectEmployee”的PK是什么? –

回答

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(); 
+1

此刻我不在乎它的完美性,casuse它的工作原理!非常感谢。它不值得寻找理想的解决方案。我浪费了很多时间。有时最简单的方法是最好的;) – Kuba

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)。如果您没有在您的上下文中明确指定此设置,只需添加它即可。

+0

我试过这个。不幸的是,它返回相同的错误。我确信当pro.ProjectEmployee清空数据库集合时,Clear()可以正常工作。 – Kuba

+0

清除内存中的集合。但是,请检查记录是否真的从数据库中删除。你能证实这一点吗? –

+0

他们是。我认为,正如IvanStoev所说,这是跟踪变更的问题。即使行被删除也不知道 – Kuba

1

another SO question

我可以在这里回答你的课程。

然后用它看起来像这样

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