Entity Framework Core: Fail to update Entity with nested value objects

前端 未结 2 1329
暗喜
暗喜 2020-12-16 03:04

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

相关标签:
2条回答
  • 2020-12-16 03:44

    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();.

    0 讨论(0)
  • 2020-12-16 03:46

    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);
    
    0 讨论(0)
提交回复
热议问题