1

I am using EF6 and Mapster in an ASP.Net MVC project. In my Edit post controller I am trying to save the results of a data entry form. On the form there is a multiselect list box. The selections in the multiselect list box are getting passed along properly but I am not understanding the correct way to save the selected items in the multiselect list to the db. Using the code as I have it below the DisplayName property is saved but the Teams are not being saved. (the Teams table has a many to many relationship to the myRecord table)

The controller code to save the changes:

 var myRecord  = TypeAdapter.Adapt<MyRecord>(myRecordViewModel);
     myRecord.Teams            = db.Teams.Where(a => myRecordViewModel .SelectedTeamIDs.Contains(a.TeamID)).ToList();               
     myRecord.DisplayName      = myRecordViewModel.Name;
     db.Entry(myRecord).State  = EntityState.Modified;
     db.SaveChanges();

The myRecord class:

 public class MyRecord
{
    [Key]
    public int Id { get; set; }
    public string DisplayName { get; set; }
    public virtual ICollection<Team> Teams { get; set; }
}

The Team class:

 public partial class Team
{
    public int TeamID { get; set; }
    public string TeamName { get; set; }
    public virtual ICollection<MyRecord> MyRecords{ get; set; }
}

How should this be saved to the database?

John S
  • 7,909
  • 21
  • 77
  • 145

1 Answers1

4

Setting the State to Modified (which attaches the entity to the context and marks all primitive properties as modified) does work for simple scenarios, but not when you have related data.

Here is the sequence of operations which does additional database trip, but allows EF change tracker to correctly identify the database operations needed to apply the changes:

(1) Load the original entity from the database, including the collection (2) Update the master information (3) Replace the collection with the new data (4) Save

var myRecord = TypeAdapter.Adapt<MyRecord>(myRecordViewModel);
myRecord.DisplayName = myRecordViewModel.Name;
// (1)
var dbRecord = db.MyRecords.Include(x => x.Teams).FirstOrDefault(x => x.Id == myRecord.Id);
// (2)
db.Entry(dbRecord).CurrentValues.SetValues(myRecord);
// (3)
dbRecord.Teams = db.Teams.Where(a => myRecordViewModel .SelectedTeamIDs.Contains(a.TeamID)).ToList();
// (4)
db.SaveChanges();
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • I tried your solution but can't get it to work. Maybe I am missing something? Please help https://stackoverflow.com/q/67645898/6156888 – Patee Gutee May 22 '21 at 05:03
  • 1
    @PateeGutee The approach here is specifically for updating many-to-many collection, which contains simple links. It cannot be used to update child data collection. – Ivan Stoev May 22 '21 at 06:11
  • What is the best practice for updating child collections? Remove the old data and add the updated ones? I'm new to this any help is much appreciated. Thanks. – Patee Gutee May 22 '21 at 18:36
  • 1
    @PateeGutee Something like that. But you have to determine that yourself - no out-of-the-box help from EF Core. Thus it might be better to look at some 3rd party extension - for instance [AutoMapper.Collection.EntityFrameworkCore](https://github.com/AutoMapper/AutoMapper.Collection.EFCore) or [Detached Mapper](https://learn.microsoft.com/en-us/ef/core/extensions/#detached-mapper) or similar. – Ivan Stoev May 24 '21 at 14:29