Estoy construyendo un webapi simple con Entity Framework Core. Estoy usando modelos y modelos de vista para administrar qué datos recibe realmente el cliente. Aquí están los modelos y los modelos de vista que creé:
public class Team : BaseEntity { [Key] public int TeamId { get; set; } [Required] public string TeamName { get; set; } public List<TeamAgent> TeamAgents { get; set; } } public class TeamViewModel { [Required] public int TeamId { get; set; } [Required] public string TeamName { get; set; } [DataType(DataType.Date)] public DateTime DateCreated { get; set; } [DataType(DataType.Date)] public DateTime DateModified { get; set; } public List<TeamAgent> TeamAgents { get; set; } } public class TeamAgent : BaseEntity { [Key] public int TeamAgentId { get; set; } [ForeignKey("Agent")] public int AgentId { get; set; } [JsonIgnore] public virtual Agent Agent { get; set; } [ForeignKey("Team")] public int TeamId { get; set; } [JsonIgnore] public virtual Team Team { get; set; } [Required] public string Token { get; set; } } public class TeamAgentViewModel { [Required] public virtual AgentViewModel Agent { get; set; } [Required] public string Token { get; set; } }Ahora, para actualizar, creé un método de actualización en mi controlador:
[HttpPut("{id}")] public async Task<IActionResult> Update(int id, [FromBody]TeamViewModel teamVM) { if (ModelState.IsValid) { var team = await _context.Teams .Include(t => t.TeamAgents) .SingleOrDefaultAsync(c => c.TeamId == id); team.TeamName = teamVM.TeamName; // HOW TO HANDLE IF SOME TEAMAGENTS GOT ADDED OR REMOVED??? _context.Teams.Update(team); await _context.SaveChangesAsync(); return new NoContentResult(); } return BadRequest(ModelState); }Me quedé atascado en el problema de cómo actualizar los TeamAgents conectados al equipo. Una cosa que probé y trabajé fue eliminar todos los TeamAgents y luego crear nuevos cada vez que se actualizan los datos del equipo. Así es cómo:
team.TeamAgents.Clear(); await _context.SaveChangesAsync(); team.TeamAgents.AddRange(teamVM.TeamAgents); _context.Teams.Update(team); await _context.SaveChangesAsync();Pero esta claramente no es una muy buena manera de hacerlo. ¿Cuál es la forma correcta de actualizar los elementos relacionados con Entity Framework Core?
Julie Lerman aborda esto en su artículo Manejo del estado de las entidades desconectadas en EF de abril de 2016. Si no ha visto el artículo, vale la pena leerlo. Lamentablemente, a partir de EF Core 2.0 todavía no hay una forma integrada de actualizar gráficos de objetos.
El enfoque mencionado en el artículo de Julie es básicamente rastrear el estado de las entidades separadas agregando una propiedad a sus objetos y enviando el estado al cliente. El cliente puede modificar el estado y enviarlo de regreso al servidor, y luego, por supuesto, puede usar esa información para hacer lo correcto.
En mi proyecto más reciente, adopté un enfoque ligeramente diferente, principalmente debido a que solo tengo una operación hasta ahora que necesita actualizar una colección secundaria. En el momento en que tenga que hacer esto de nuevo, probablemente tomaré en serio las sugerencias de Julie y las refactorizaré.
Básicamente, es solo una actualización manual del gráfico de objetos, que se parece bastante al enfoque que tomaría con EF 6.0. Una cosa a tener en cuenta es que ahora con EF Core puede simplemente llamar a Remove() pasando la entidad, sin tener que preocuparse a qué dbSet pertenece.
/// <param name="entity"></param> public override void Update(Group entity) { // entity as it currently exists in the db var group = DbContext.Groups.Include(c => c.Contacts) .FirstOrDefault(g => g.Id == entity.Id); // update properties on the parent DbContext.Entry(group).CurrentValues.SetValues(entity); // remove or update child collection items var groupContacts = group.Contacts.ToList(); foreach (var groupContact in groupContacts) { var contact = entity.Contacts.SingleOrDefault(i => i.ContactId == groupContact.ContactId); if (contact != null) DbContext.Entry(groupContact).CurrentValues.SetValues(contact); else DbContext.Remove(groupContact); } // add the new items foreach (var contact in entity.Contacts) { if (groupContacts.All(i => i.Id != contact.Id)) { group.Contacts.Add(contact); } } DbContext.SaveChanges(); }Créditos a @Slauma por esto.
public void Update(UpdateParentModel model) { var existingParent = _dbContext.Parents .Where(p => p.Id == model.Id) .Include(p => p.Children) .SingleOrDefault(); if (existingParent != null) { // Update parent _dbContext.Entry(existingParent).CurrentValues.SetValues(model); // Delete children foreach (var existingChild in existingParent.Children.ToList()) { if (!model.Children.Any(c => c.Id == existingChild.Id)) _dbContext.Children.Remove(existingChild); } // Update and Insert children foreach (var childModel in model.Children) { var existingChild = existingParent.Children .Where(c => c.Id == childModel.Id && c.Id != default(int)) .SingleOrDefault(); if (existingChild != null) // Update child _dbContext.Entry(existingChild).CurrentValues.SetValues(childModel); else { // Insert child var newChild = new Child { Data = childModel.Data, //... }; existingParent.Children.Add(newChild); } } _dbContext.SaveChanges(); } }Fuente: