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...