У меня возник вопрос о EF 6.0 после извлечения элемента из БД с EF (FirstOrDefault), изменяющего значение, а затем запроса одного и того же элемента в другой коллекции с предложением Where.
Например, предположим, что класс Foo имеет 3 свойства: Id, Desc, StatusId, где Foos - EntitySet. Первоначально после первой выборки:
var item = Foos.FirstOrDefault(f => f.Id = 5);
// item values are: Id:5, Desc:"Whatever", StatusId:1 after fetching from database
// Change statusId
item.StatusId = 5
// statusItems will not contain above object HOWEVER...
var statusItems = Foos.Where(f => f.StatusId == 5).ToList();
// allItems will contain item, with StatusId = 5
var allItems = Foos.ToList() // Or FirstOrDefault
Ожидается ли такое поведение от EF? Если да, можете ли вы заставить предложение where работать над прикрепленными объектами в DbContext, не указав сначала.ToList()?
Возможное исправление, которое я нашел для вышеизложенного:
var item = Foos.FirstOrDefault(f => f.Id = 5);
// item value are: Id:5, Desc:"Whatever", StatusId: 1 after fetching from database
// Change statusId
item.StatusId = 5
// statusItems will now contain item
var statusItems = Foos.ToList().Where(f => f.StatusId == 5).ToList();
Другой способ обойти это - обернуть его в транзакцию, которую я предполагаю, и вызвать SaveChanges после изменения свойства StatusId.
Я полагаю, что нет никакого способа обойти это, не зная, как это работает (и как таковые пытаются отфильтровать другое свойство, которое не изменилось, чтобы убедиться, что вы не перетаскиваете всю таблицу клиенту до фильтрации)?
Случается, что MergeOption, который вы используете, скорее всего AppendOnly
(значение по умолчанию) имеет определенное поведение. Entity Framework содержит список объектов, уже реализованных в памяти. Чтобы проиллюстрировать, что происходит:
var item = Foos.FirstOrDefault(f => f.Id = 5);
В инфраструктуре Entity теперь есть (Id = 5) в памяти
var statusItems = Foos.Where(f => f.StatusId == 5).ToList();
Все элементы извлекаются из базы данных, в которой есть StatusId = 5 в базе данных! Это не включает объект с (Id = 5), потому что изменения не были синхронизированы с базой данных, но с помощью SaveChanges
.
var allItems = Foos.ToList();
Теперь у вас есть список всех элементов из таблицы. Параметр AppendOnly
merge выполняет следующие действия:
Id=1 - Materialize object
Id=2 - Materialize object
...
Id=5 - Already exists! Give the existing instance
...
Вывод: при использовании опции AppendOnly
существуют два состояния, состояние базы данных и состояние кэша структуры сущности. Если вы запрашиваете список Foos
он всегда будет поступать в базу данных, но возвращенные объекты сопоставляются с первичным ключом с тем, что уже существует. Если существующий объект найден, этот экземпляр будет возвращен.
Это также объясняет, почему ваша вторая ситуация вернет объект, о котором идет речь, вы сначала извлекаете всю таблицу, а затем фильтруете ее.
Без дополнительного контекста трудно сделать предложение для решения этой проблемы. Скорее всего, вы хотите сохранить ранее.
Заявление...
var statusItems = Foos.Where(f => f.StatusId == 5)
... всегда идет в базу данных. SQL-запрос возвращает объект, имеющий StatusId == 5
в базе данных. Объект item
не был сохранен еще, так что она не включена.
Итак, что вы здесь сделали, это получить объект с некоторым статусом (вероятно, не 5), изменив его на StatusId = 5, а затем выберет больше объекта, у которого уже был StatusId = 5. Число элементов в контексте теперь представляет собой количество объектов из последний запрос + 1.
вы можете заставить предложение where работать над прикрепленными объектами в DataContext?
(Кстати, DbContext) Да, запросив локальную коллекцию:
Foos.Local.Where(f => f.StatusId == 5)
В этой ситуации этот оператор вернет все элементы Foo
которые у вас есть в контексте до сих пор.
Когда вы выполняете Foos.ToList()
, все форы будут извлечены из базы данных. По умолчанию EF не будет перезаписывать элементы, которые он уже отслеживает. В конце концов, вы можете потерять измененный вы сделали. Таким образом, это утверждение добавит новые элементы foo в коллекцию Local
которые еще не были.