Почему Entity Framework 6.x не кэширует результаты?
Возможно, я недопонимаю кэширование, что DbContext
и DbSet
делает, но у меня создалось впечатление, что есть некоторое кэширование, которое будет продолжаться. Я вижу поведение, которого я не ожидал бы при запуске следующего кода:
var ctx = CreateAContext();
var sampleEntityId = ctx.SampleEntities.Select(i => i.Id)
.Single(i => i == 3); //Calls DB as expected
var cachedEntityId = ctx.SampleEntities.Select(i => i.Id)
.Single(i => i == 3); //Calls DB unexpectedly
Что здесь происходит? Я думал, что часть того, что вы получаете от DbSet
, состоит в том, что сначала будет проверять локальный кеш, чтобы увидеть, существует ли этот объект перед запросом базы данных. Есть ли какой-то вариант конфигурации, который я здесь отсутствует?
Ответы
Ответ 1
Что @emcas88 пытается сказать, заключается в том, что EF будет проверять кеш только при использовании метода .Find
на DbSet
.
Использование .Single
, .First
, .Where
и т.д. не будет кэшировать результаты, если вы не используете кэширование второго уровня.
Ответ 2
Это связано с тем, что при реализации методов экстензора используется метод Find контекста
contextName.YourTableName.Find()
чтобы сначала проверить кеш. Надеюсь, что это поможет.
Ответ 3
Иногда я использую свой метод расширения:
using System.Linq;
using System.Linq.Expressions;
namespace System.Data.Entity
{
public static class DbSetExtensions
{
public static TEntity FirstOrDefaultCache<TEntity>(this DbSet<TEntity> queryable, Expression<Func<TEntity, bool>> condition)
where TEntity : class
{
return queryable
.Local.FirstOrDefault(condition.Compile()) // find in local cache
?? queryable.FirstOrDefault(condition); // if local cache returns null check the db
}
}
}
Использование:
db.Invoices.FirstOrDefaultCache(x => x.CustomerName == "Some name");
Вы также можете заменить FirstOrDefault на SingleOrDetfault.
Ответ 4
Взгляните на EF Docs, вы найдете там ответ:
Обратите внимание, что DbSet и IDbSet всегда создают запросы к базе данных и всегда будет включать обратную поездку в базу данных, даже если возвращаемые объекты уже существуют в контексте. Выполняется запрос. против базы данных, когда:
- Он перечисляется оператором foreach (С#) или для каждого (Visual Basic).
- Он перечисляется операцией сбора, такой как
ToArray
, ToDictionary
или ToList
. - Операторы LINQ, такие как
First
или Any
, указаны в самой внешней части запроса. - Вызываются следующие методы: метод расширения
Load
на DbSet
, DbEntityEntry.Reload
и Database.ExecuteSqlCommand
.
Ответ 5
EF6 не выполняет кеширование результатов. Чтобы кэшировать результаты, вам необходимо использовать кеш второго уровня. Посмотрите этот перспективный проект на CodePlex:
Кэширование второго уровня для EF 6.1
Имейте в виду, что если данные изменятся на db, вы не сразу узнаете об этом. Иногда это важно в зависимости от проекта.;)
Ответ 6
Это очень понятно на MSDN. Обратите внимание, что найти
[https://docs.microsoft.com/en-us/ef/ef6/querying/][1]
using (var context = new BloggingContext())
{
// Will hit the database
var blog = context.Blogs.Find(3);
// Will return the same instance without hitting the database
var blogAgain = context.Blogs.Find(3);
context.Blogs.Add(new Blog { Id = -1 });
// Will find the new blog even though it does not exist in the database
var newBlog = context.Blogs.Find(-1);
// Will find a User which has a string primary key
var user = context.Users.Find("johndoe1987");
}