Свойство навигации загружено не полностью после нетерпеливой загрузки

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

using (DBContext db = new MyContextClass())
{
    ... data creation ... 
    db.SaveChanges();
}

using (DBContext db = new MyContextClass())
{
     db.Configuration.LazyLoadingEnabled = false;
     db.Configuration.ProxyCreationEnabled = false;
     db.Configuration.AutoDetectChangesEnabled = false;

     DbQuery data = db.Set(someType)
     foreach (string propertyName op in somePropertieNames)
                data = data.Include(propertyName);

     foreach (object item in data.AsNoTracking())
         ScanNavigatorsOf(item);
}

в ScanNavigatorsOf() я прочитал все свойства навигации и заметил, что первый объект, выданный циклом foreach, имеет неполные коллекции и ссылки. Кажется, что заполнение свойств навигации еще не завершено, когда программа обращается к моему методу ScanNavigatorsOf(...). Все остальные объекты имеют полные свойства навигации. Я запускаю для этого модульные тесты, чтобы убедиться, что объекты хорошо хранятся в базе данных.

Как я могу дождаться полного заполнения навигационных коллекций и ссылок загруженных объектов?

Как описано в событии ObjectContext.ObjectMaterialized кажется, что коллекции не материализуются одновременно с объектом main, но как узнать, когда процесс завершен?


person Dubbs777    schedule 15.06.2014    source источник


Ответы (1)


Я почти уверен, что вы испытываете побочные эффекты при отключении DetectChanges. Это вызов, который EF делает много раз под поверхностью, чтобы убедиться, что все ассоциации между отслеживаемыми объектами установлены правильно и соответствуют примитивные значения внешнего ключа (это называется исправлением отношений). У вас может быть причина его отключить, но, как говорят Lerman & Miller в своей книге DbContext:

Выяснить, когда нужно вызывать DetectChanges, не так просто, как может показаться. Команда Entity Framework настоятельно рекомендует переключаться на ручной вызов DetectChanges только в том случае, если у вас возникли проблемы с производительностью. Также рекомендуется отказаться от автоматического DetectChanges только для плохо работающих разделов кода и повторно включить его после завершения выполнения рассматриваемого раздела.

Кроме того, вы получаете объекты без отслеживания. Теперь EF почти полностью искалечен, когда дело доходит до ассоциаций.

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

 foreach (object item in data)
 {
     db.ChangeTracker.DetectChanges();
     ScanNavigatorsOf(item);
 }

Или включите AutoDetectChangesEnabled перед запуском цикла, когда в ScanNavigatorsOf будет запущен один из этих многочисленных методов, запускающих DetectChanges.

Но в AsNoTracking есть еще одна проблема. Эффект от отключения отслеживания изменений заключается в том, что контекст не может служить картой идентичности, что означает: он не может гарантировать, что каждая запись в базе данных будет материализована ровно один раз.

Если в вашем Includes есть ассоциация 1-n-1, скажем, Order-OrderLine-Product, без отслеживания для каждого OrderLine будет создан новый экземпляр Product, даже если "тот же" продукт уже был создан для предыдущей строки заказа. Это не большая проблема, если вы используете данные только для чтения, но как только вы собираетесь прикрепить их к контексту (например, для репликации), вы столкнетесь с повторяющимися ключевыми исключениями.

Короче говоря: когда ассоциации имеют значение, позвольте системе отслеживания изменений сделать свою работу и убедитесь, что DetectChanges вызывается вовремя.

person Gert Arnold    schedule 15.06.2014