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?
Update Objects in Entity Framework 4.0First retrieve an instance of the entity from the EntitySet<T> (in our case ObjectSet<Customer>), then edit the properties of the Entity and finally call SaveChanges() on the context.
We can update records either in connected or disconnected scenarios. In the connected Scenario, we open the context, query for the entity, edit it, and call the SaveChanges method. In the Disconnected scenario, we already have the entity with use. Hence all we need to is to attach/add it to the context.
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();
}
Credits to @Slauma for this.
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();
}
}
Source:
https://stackoverflow.com/a/27177623/3850405
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With