EF Core 10 has no built-in second-level cache — the change tracker is only a first-level identity map. EFCoreSecondLevelCacheInterceptor v5.5.0 fills that gap by intercepting queries and caching results keyed by SQL. In v5, cache providers are separate NuGet packages (MemoryCache, StackExchange.Redis, or HybridCache). Setup involves installing the right provider, registering it in Program.cs, and adding the interceptor to DbContext. Queries can be cached per-query with .Cacheable() or globally with CacheAllQueries(). Benchmarks show in-memory hits are ~11x faster than a DB round trip; Redis hits are ~2.5x faster. Auto-invalidation only works for SaveChanges — ExecuteUpdate, ExecuteDelete, raw SQL, and external writes bypass it and require manual invalidation via IEFCacheServiceProvider, with the correct cache key prefix. In-memory caches are per-instance, so multi-instance deployments need Redis. Multi-tenant apps risk key collisions unless the tenant ID is a real SQL parameter and per-tenant key prefixes are used. Best suited for read-heavy reference data; avoid for volatile, per-user, or compliance-sensitive data.

•18m read time•From codewithmukesh.com
Post cover image
Table of contents
EF Core Interceptors: The Complete GuideWhat Is Second-Level Caching in EF Core?Tracking vs. No-Tracking Queries in EF Core 10Does EF Core Have a Built-In Second-Level Cache?How Do I Add Second-Level Caching in EF Core 10?How Do I Use Redis as a Second-Level Cache?HybridCache in ASP.NET CoreSecond-Level Cache Benchmark: How Much Does a Hit Save?Why Does a Cached Query Return Stale Data?Bulk Operations in EF Core 10Global Query Filters in EF CoreWhen Should You Not Use Second-Level Caching?10 EF Core Performance Mistakes (and How to Fix Them)Second-Level Cache vs HybridCache vs Manual CachingIn-Memory Caching in ASP.NET CoreHow Do I Verify the Cache Is Actually Hitting?Distributed Caching in ASP.NET Core with RedisKey TakeawaysTroubleshootingFAQSummary

Questions this post answers

Does EF Core 10 have a built-in second-level cache?

No. EF Core 10's only built-in cache is the change tracker, a first-level identity map scoped to a single DbContext that resolves already-tracked entities by key but does not cache query results; ordinary LINQ queries still hit the database every time. To cache query results across requests you need a third-party library such as EFCoreSecondLevelCacheInterceptor version 5.5.0, paired with a separate provider package like MemoryCache, StackExchange.Redis, or HybridCache. Developers deciding whether to add caching infrastructure to an EF Core app can track library specifics like this on daily.dev.

Why does my EFCoreSecondLevelCacheInterceptor cache still return old data after an ExecuteUpdate call?

ExecuteUpdate and ExecuteDelete issue SQL directly without going through SaveChanges, so the interceptor's automatic invalidation, which only inspects the change tracker on SaveChanges, never sees them and keeps serving stale cached rows until the TTL expires. The same gap applies to raw SQL and writes from other services. Fix it by injecting IEFCacheServiceProvider and calling InvalidateCacheDependencies or ClearAllCachedEntries right after the write. Anyone debugging stale reads after a bulk update can check invalidation edge cases like this via daily.dev.

How much faster is a Redis or in-memory second-level cache hit compared to a normal EF Core database query?

In a BenchmarkDotNet test against 5,000 seeded rows on PostgreSQL 17 and Redis 7 in Docker, an uncached database round trip averaged 1,440 microseconds, an in-memory second-level cache hit averaged 129 microseconds (about 11x faster), and a Redis cache hit averaged 569 microseconds (about 2.5x faster) because it pays a network hop plus serialization on top of the in-memory savings. Teams weighing in-memory versus Redis caching for EF Core can compare benchmark numbers like these on daily.dev.

Share this post