Updating related data with Entity Framework Core

asp.net-core-webapi asp.net-web-api c# entity-framework-core

Question

Im building a simple webapi with Entity Framework Core. I am using models and viewmodels to manage what data the client is actually receiving. Here's the models and viewmodels i created:

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

Now for updating i created a Update method in my controller:

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

I got myself stuck at the problem how to update the TeamAgents connected to the Team. One thing what i tried and worked was deleting all the TeamAgents and then just creating new ones every time Team data is updated. Here's how:

team.TeamAgents.Clear();
await _context.SaveChangesAsync();
team.TeamAgents.AddRange(teamVM.TeamAgents);

_context.Teams.Update(team);
await _context.SaveChangesAsync();

But this clearly is not very good way to do it. What is the right way to update the related items with Entity Framework Core?

1
6
3/11/2017 6:55:35 PM

Popular Answer

Julie Lerman addresses this in her article Handling the State of Disconnected Entities in EF from April of 2016. If you haven't seen the article it is well worth the read. Sadly, as of EF Core 2.0 there is still no built in way to update object graphs.

The approach mentioned in Julie's article is to basically track the state of the detached entities by adding a property to your objects and sending the state along to the client. The client can modify state and send that back to the server, and then of course you can use that information to do the right thing.

In my most recent project I've taken a slightly different approach mainly due to only having one operation so far that needs to update a child collection. At the point which I have to do this again, I'll probably take Julie's suggestions to heart and refactor.

Basically it's just a manual object graph update, which looks pretty similar to the approach you'd take with EF 6.0. One thing to note is that now with EF Core you can just call Remove() passing the entity, not having to care what dbSet it belongs to.

/// <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();
}
7
11/5/2017 8:03:57 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow