EF 6.0 неправильные позиции после предложения where

1

У меня возник вопрос о 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.

Я полагаю, что нет никакого способа обойти это, не зная, как это работает (и как таковые пытаются отфильтровать другое свойство, которое не изменилось, чтобы убедиться, что вы не перетаскиваете всю таблицу клиенту до фильтрации)?

Теги:
entity-framework

2 ответа

3

Случается, что 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 он всегда будет поступать в базу данных, но возвращенные объекты сопоставляются с первичным ключом с тем, что уже существует. Если существующий объект найден, этот экземпляр будет возвращен.

Это также объясняет, почему ваша вторая ситуация вернет объект, о котором идет речь, вы сначала извлекаете всю таблицу, а затем фильтруете ее.

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

  • 0
    У меня есть элемент с 2 ключами PK (Id и Version), поэтому я использовал его для фильтрации коллекции, и тогда она найдет нужные элементы. Мне просто было интересно, есть ли другой способ обойти это вместо сохранения или использования большей коллекции. Тем не менее, чем больше вы думаете об этом, тем больше это имеет смысл с точки зрения эффективности. Поначалу немного странно видеть, что он отключен в настройках MergeOptions по умолчанию.
1

Заявление...

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 которые еще не были.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню