Entity Framework не обнаружит изменений свойств навигации

У меня возникают проблемы с обнаружением изменений свойства навигации:

Моя модель тестирования выглядит так:

public class Person
{
    public int Id { get; set; }

    public string Name { get; set; }

    public virtual Address Address { get; set; }
}

public class Address
{
    public int Id { get; set; }

    public string Name { get; set; }
}

Я создал и сохранил объект типа Person с назначенными свойствами Name и Address. Моя проблема в том, что если я извлечу объект Person из базы данных, и я изменил свойство Address (например, на Null), то e.f. не обнаруживает изменения! Мой код:

using (var ctx = new EFContext())
{
    Person p = ctx.People.First();
    //p.Address IS NOT NULL!
    p.Address = null;
    var entry = ctx.Entry(p);
}

Почему entry.State Без изменений?

Изменить: Если я вызову SaveChanges, запись будет сохранена правильно (адрес станет нулевым)!

Изменить 2:. Я создал свойство внешнего ключа, как предложил billy, и если я проверю объект Person в визуальной студии, состояние будет изменено. Если я не остановлюсь с проверкой отладчика значения объекта не изменяются!

Изменить 3: Загрузка объекта Person с помощью ctx.People.Include(x = > x.Address).First(); решает проблему. Есть ли способ избежать вызова Include и продолжить изменение свойства Address вместо AddressId?

Ответы

Ответ 1

Прежде всего: вы ДОЛЖНЫ следовать совету @billy, чтобы использовать Include. Ваше замечание "p.Address IS NOT NULL!" это правда, потому что вы наблюдаете p.Address в отладчике и тем самым запускаете ленивую загрузку в отладчике, поэтому обнаружено изменение установки адреса на null. В режиме деблокирования или когда вы не проверяете свойства в отладчике, ваш код не будет работать и никаких изменений не будет сохранено.

Итак, ответ на ваш Редактор 3: No.

Во-вторых: var entry = ctx.Entry(p) возвращает только состояния сущностей, и вы не изменили состояние объекта, а вместо этого состояние отношения или, точнее, вы удалили связь. Вы не можете проверять состояния отношений с API DbContext, но только с ObjectContext API:

Person p = ctx.People.Include(x => x.Address).First();
p.Address = null;
var objCtx = ((IObjectContextAdapter)ctx).ObjectContext;
var objentr = objCtx.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted);

objentr теперь будет иметь тип RelationshipEntry:

enter image description here

EF рассмотрит эту запись отношения вместе с записями состояния объекта, когда вы вызываете SaveChanges() и удаляете взаимосвязь, то есть установите столбец внешнего столбца Address Person в базе данных на null.

Об изменении 2: изменение свойства внешнего ключа (которое является скалярным свойством в вашей модели) - это изменение самого объекта, поэтому в этом случае состояние объекта будет Modified.

Ответ 2

Вам нужно указать адрес nav. двигательный в вашем запросе, иначе EF не будет учитывать изменения в нем при сохранении:

using (var ctx = new EFContext())
{
    Person p = ctx.People.Include(x => x.Address).First();
    //p.Address IS NOT NULL!
    p.Address = null;
    var entry = ctx.Entry(p);
}

Вы также можете использовать внешние ключи в своей модели, которые мне очень нравятся:

public class Person
{
    public int Id { get; set; }

    public string Name { get; set; }

    public virtual Address Address { get; set; }

    public int? AddressId {get; set;}
}

...

using (var ctx = new EFContext())
{
    Person p = ctx.People.First();
    p.AddressId = null;
    var entry = ctx.Entry(p);
}

Ответ 3

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

Это в основном работает с принятым в настоящее время ответом, но я хотел бы предоставить реализацию и обратить внимание на то, что вы должны называть Context.ChangeTracker.DetectChanges(), прежде чем ObjectContext.ObjectStateManager сможет забрать изменения отношений! Я потратил немало времени на отладку, глупо!

_EagleContext.ChangeTracker.DetectChanges();

var objectContext = ((IObjectContextAdapter)_EagleContext).ObjectContext;
var changedEntities = objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified);

if (_EagleContext.ChangeTracker.Entries().Any(e => e.State == EntityState.Modified)
    || changedEntities.Count() != 0)
{
    var dialogResult = MessageBox.Show("There are changes to save, are you sure you want to reload?", "Warning", MessageBoxButton.YesNo);
    if (dialogResult == MessageBoxResult.No)
    {
        return;
    }
}

// Continue with reloading...