Кэширует ли EF объекты между разными экземплярами DbContext?

Заставляет ли создание DbContext для каждого запроса в Asp.net EF только считывать данные из своего кеша или каждый раз запрашивает у БД все наборы? Я знаю о кэшировании метаданных для каждого AppDomain, но как насчет данных?

Контекст: приложение для сбора и визуализации данных с интерфейсом MVC4 + Web API, которое нельзя назвать «большим объемом», но многие запросы возвращают одни и те же наборы данных в более короткие сроки.


person mrówa    schedule 11.06.2013    source источник
comment
Я не думаю, что данные кэшируются в сущностях, это всегда запрос БД   -  person Jalpesh Vadgama    schedule 11.06.2013


Ответы (1)


Entity Framework не имеет кеша данных для каждого AppDomain, только кеш для каждого экземпляра контекста.

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

Более того, термин «кеш для каждого экземпляра контекста» может вводить в заблуждение, поскольку он не означает, что EF не будет выполнять запросы к базе данных, если сущности уже загружены в кэш контекста. Вот как работает этот кеш и как вы можете его использовать (или нет):

  • Каждый запрос LINQ-to-Entities к DbSet<T> или вообще к IQueryable<T> будет запускать запрос к базе данных, независимо от того, существуют ли уже сущности в контексте или нет. Но если сущность с тем же ключом, что и запрошенная сущность, уже существует в контексте, EF отбросит результат этого запроса и вернет кэшированный экземпляр сущности обратно вызывающей стороне.

    Это проверяет, существует ли объект с таким же ключом после выполнения запроса. (Для сложных запросов, например запросов, содержащих Include, он не может выполнить эту проверку раньше, поскольку не может знать, какие сущности и значения ключей будут возвращены.)

    Это поведение по умолчанию (MergeOption равно AppendOnly). Я полагаю, что вы можете изменить это поведение на OverwriteChanges и другие параметры, но ни один из них не позволит избежать того, что запросы LINQ всегда вызывают запросы к базе данных.

  • Для запроса объекта только по его ключу вы можете использовать GetObjectByKey или FindDbContext), которые сначала проверят, кэширован ли уже объект с этим ключом в контексте, а затем вернут этот кэшированный объект. Если нет, он запустит запрос к базе данных, чтобы загрузить его.

  • Вы можете запросить ChangeTracker EF, он особенно хорошо поддерживается с DbContext, где у вас есть доступ к кешу контекста через коллекцию DbSet<T>.Local.

    Проблема здесь в том, что нет никакой логики для автоматического запроса к базе данных, если запрос на Local не возвращает результат. Вы должны написать эту логику вручную. Еще большая проблема заключается в том, что запрос на Local является LINQ-to-Objects, а не LINQ-to-Entities (Local не реализует IQueryable<T>, только IEnumerable<T>), поэтому вам часто приходится переписывать свои запросы, чтобы воздействовать на Local, например вы не можете использовать Include здесь, вы не можете использовать EntityFunctions, вы получите другое поведение для сравнения строк в отношении чувствительности к регистру и т. д. и т. д.

person Slauma    schedule 11.06.2013
comment
Спасибо за отличное объяснение. До сих пор я полностью упускал из виду тот факт, что кэшированная версия может использоваться, даже если запросы к базе данных выполняются. Я использовал SQL Server Profiler и видел запросы, которые не возвращали свежие данные в моем приложении!? Наконец-то я узнал, что мой пользовательский MembershipProvider сохранялся фреймворком между запросами, как и экземпляр контекста, который он сохранял. Другой код выполнялся в контексте, и все полностью запуталось. - person R. Schreurs; 02.07.2013