Entity Framework Core: не удалось обновить сущность с вложенными объектами значений - PullRequest
0 голосов
/ 23 мая 2018

У меня есть объект, у которого есть объект значения, а у этого объекта значения есть другой объект значения.Моя проблема заключается в том, что при обновлении сущности вместе с объектами значения сущность с родительским объектом значения обновляется, а дочерний объект значения - нет.обратите внимание, я использовал последнюю версию Entity Framework Core 2.1.0-rc1-final, это родительский объект Employee

public class Employee : Entity
{
    public string FirstName { get; private set; }
    public string LastName { get; private set; }
    public string Email { get; private set; }
    public Address Address { get; private set; }
}

, а это родительский объект значения Address

public class Address : ValueObject<Address>
{
    private Address() { }

    public Address(string street, string city, string state, string country, string zipcode, GeoLocation geoLocation)
    {
        Street = street;
        City = city;
        State = state;
        Country = country;
        ZipCode = zipcode;
        GeoLocation = geoLocation;
    }

    public string Street { get; private set; }
    public string City { get; private set; }
    public string State { get; private set; }
    public string Country { get; private set; }
    public string ZipCode { get; private set; }
    public GeoLocation GeoLocation { get; private set; }
}

иэто дочерний объект значения GeoLocation

public class GeoLocation
{
    private GeoLocation()
    {

    }

    public GeoLocation(decimal longitude, decimal latitude)
    {
        Latitude = latitude;
        Longitude = longitude;
    }
    public Decimal Longitude { get; private set; }
    public Decimal Latitude { get; private set; }
}

, и при обновлении сотрудника я сначала получаю его из базы данных, затем изменяю свойство Address, используя новое значение, полученное из пользовательского интерфейса

var employee = _repository.GetEmployee(empId);
employee.SetAddress(newAddress);

и метод SetAddress

public void SetAddress(Address address)
{
    Guard.AssertArgumentNotNull(address, nameof(address));
    Address = address;
}

1 Ответ

0 голосов
/ 23 мая 2018

В соответствии с этим билетом EF Core GitHub необходимо обновить свойства дочернего / вложенного / собственного типа, чтобы он правильно отслеживался.Предполагалось, что это будет исправлено в EF 2.1 (в настоящее время доступно только в качестве кандидата на выпуск), но, возможно, оно не было сделано.В 2.0.3 они обновили словесность исключения:

InvalidOperationException: экземпляр типа сущности 'Parent.Child # Child' не может быть отслежен, поскольку другой экземпляр с тем же значением ключа для {'ParentID '} уже отслеживается. При замене собственных объектов измените свойства без изменения экземпляра или сначала отсоедините предыдущую запись принадлежащего объекта.

Вторая часть этого сообщения заставит васбросить немного, если вы используете DDD.Это говорит о том, что вы должны обновить свойства дочерних / вложенных свойств непосредственно для EF, чтобы правильно отслеживать изменения (что нарушает DDD Value Objects как неизменные).Что касается комментария к ветке GitHub , здесь предлагается несколько обходных путей, подходящих для DDD, адаптированных под ваш код:

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-

Второй предложенный обходной путь выполняется путем отсоединения сущности, обновления экземпляра Address и последующего его присоединения.Мне не очень повезло с этим обходным путем в моей реализации, но я опубликую его для будущих поколений.Возможно, вам повезет больше, чем мне.

context.Entry(employee.Address).State = EntityState.Detached;
employee.SetAddress(newAddress);
context.Entry(employee.Address).State = EntityState.Modified;

ОБНОВЛЕНИЕ

Я наконец нашел открытый билет с командой EF Core, который можно отследитьдля этого вопроса. В билете # 10551 конкретно указана проблема, которая по-прежнему остается открытой.Он определенно не попал в EF Core 2.1 и, похоже, был помещен в Backlog Milestone 3.0.Обратите внимание, что вы можете проголосовать за эту проблему, чтобы заставить команду EF Core уделять ей больше внимания.

ОБНОВЛЕНИЕ 2 В EF Core 2.2 представлен компонент Tracked Graph, который делает это намногобольше жидкостиЭто, однако, требует, чтобы все ваши объекты EF использовали идентификаторы, сгенерированные базой данных.Этот метод проверяет, установлен ли ключ объекта, а затем отмечает объект как измененный или добавленный.Это может быть расширено, чтобы включать удаления, но для моих целей я не хочу такого поведения.

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
}

Затем используйте context.Upsert(<YOUR ENTITY OBJECT>); перед context.SaveChanges();.

...