I have an entity that has a value object and this value object has another value object. My issue is that when updating the entity along with the value objects, the entity w
According to this EF Core GitHub ticket you have to update the child/nested/owned type properties directly for it to track properly. This was supposed to be fixed in EF 2.1 (currently only available as a release candidate) but may have not made the cut. In 2.0.3 they updated the verbiage of the exception to:
InvalidOperationException: The instance of entity type 'Parent.Child#Child' cannot be tracked because another instance with the same key value for {'ParentID'} is already being tracked. When replacing owned entities modify the properties without changing the instance or detach the previous owned entity entry first.
The second part of this message will make you throw up a little if you are using DDD. It is telling you that you must update the properties of the child/nested properties directly for EF to properly track the changes (which breaks DDD Value Objects as being immutable). As per a comment on the GitHub thread here is a suggested, somewhat DDD friendly, workaround adapted to match your code:
public void SetAddress(Address address)
{
Guard.AssertArgumentNotNull(address, nameof(address));
Address.UpdateFrom(address);
}
// And on Address:
internal void UpdateFrom(Address other)
{
Street = other.Street;
// ...
}
-OR-
The second suggested workaround is done by detaching the entity, updated the instance of Address
, then re-attaching it. I didn't have much luck with this workaround in my implementation, but will post it for posterity. Maybe you'll have better luck with it than I did.
context.Entry(employee.Address).State = EntityState.Detached;
employee.SetAddress(newAddress);
context.Entry(employee.Address).State = EntityState.Modified;
UPDATE
I finally found the open ticket with the EF Core team that can be tracked for this issue. Ticket #10551 specifically states the issue at hand and is still open. It definitely didn't make it to EF Core 2.1 and appears to have been placed in the Backlog Milestone 3.0. Note you can up-vote this issue as a way to get the EF Core team to put more attention on it.
UPDATE 2 EF Core 2.2 introduced a Tracked Graph component that makes this much more fluid. This does, however, require that all of your EF Entities use database generated ids. This method inspects whether the entity key is set, then flags the entity as modified or added. This can be expanded to include deletes, but for my purposes I don't want that sort of behavior.
internal void Upsert(object entity)
{
ChangeTracker.TrackGraph(entity, e =>
{
if (e.Entry.IsKeySet)
{
e.Entry.State = EntityState.Modified;
}
else
{
e.Entry.State = EntityState.Added;
}
});
#if DEBUG
foreach (var entry in ChangeTracker.Entries())
{
Debug.WriteLine($"Entity: {entry.Entity.GetType().Name} State: {entry.State.ToString()}");
}
#endif
}
Then, use the context.Upsert(<YOUR ENTITY OBJECT>);
before context.SaveChanges();
.
A simpler alternative can be found at Owned type property not persisting for a modified entity in EF Core
in short, instead of
_context.Entry(contactModelFromRequestBody).State = EntityState.Modified;
you should use
_context.Update(contactModelFromRequestBody);