2016-10-03 4 views
3

Я пытаюсь обновить коллекцию ProjectEmployees внутри ProjectModel. Я хочу удалить все старые значения и установить новые.Entity Framework Core - связанная с обновлением коллекция

Мои модели:

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. а затем попробуйте обновить его. –

+0

Мне кажется, что это конфликты с первичными ключами. Когда вы создаете новый ProjectEmployee(), вы устанавливаете только UserId. Не следует ли также установить ProjectId? –

+0

Что такое PK для 'ProjectEmployee'? –

ответ

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

На данный момент меня не волнует, если он прекрасен, он работает! Спасибо. Не стоит искать идеальное решение. Я потратил много времени на это. Иногда самые простые способы являются лучшими;) – 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

Я пробовал это. К сожалению, он возвращает ту же ошибку. Я уверен, что Clear() отлично работает, когда pro.ProjectEmployee пуст. Коллекция db очищается. – Kuba

+0

Коллекция в памяти очищается. Но, пожалуйста, проверьте, действительно ли записи удалены из базы данных. Можете ли вы это подтвердить? –

+0

Они есть. Я думаю, что, как сказал ИванСтоев, это проблема с отслеживанием изменений ef. Даже если строки удалены, он не знает об этом – Kuba

1

связанные с 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); 
Смежные вопросы