Why does Entity Framework 6.x not cache results?

2020-01-30 04:41发布

问题:

Perhaps I am misunderstanding the caching that DbContext and DbSet does but I was under the impression that there was some caching that would go on. I'm seeing behavior that I wouldn't expect when I run the following code:

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

What's going on here? I thought that part of what you get from DbSet is that it would first check the local cache to see if that object exists before querying the database. Is there just some sort of configuration option I am missing here?

回答1:

What @emcas88 is trying to say is that EF will only check the cache when you use the .Find method on DbSet.

Using .Single, .First, .Where, etc will not cache the results unless you are using second-level caching.



回答2:

This is because the implementation of the extensor methods, use the Find method of the context

contextName.YourTableName.Find()

to verify first the cache. Hope it helps.



回答3:

Sometimes I use my extension method:

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
        }
    }
}

Usage:

db.Invoices.FirstOrDefaultCache(x => x.CustomerName == "Some name");

You can replace FirstOrDefault with SingleOrDetfault also.



回答4:

Take a look at EF Docs, you will find answer there:

Note that DbSet and IDbSet always create queries against the database and will always involve a round trip to the database even if the entities returned already exist in the context. A query is executed against the database when:

  • It is enumerated by a foreach (C#) or For Each (Visual Basic) statement.
  • It is enumerated by a collection operation such as ToArray, ToDictionary, or ToList.
  • LINQ operators such as First or Any are specified in the outermost part of the query.
  • The following methods are called: the Load extension method on a DbSet, DbEntityEntry.Reload, and Database.ExecuteSqlCommand.


回答5:

EF6 doesn't do results caching ootb. In order to cache results, you need to use a second level cache. See this promising project on CodePlex:

Second Level Caching for EF 6.1

Keep in mind that if data changes on the db, you won't immediately know about it. Sometimes this is important depending upon the project. ;)



回答6:

It is very clear on MSDN. Please note what is find

[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");
}