<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/second-level-caching-in-ef-core-10-the-complete-guide-bvdnj7ehj" -->

---
title: Second-Level Caching in EF Core 10: The Complete Guide
description: 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...
canonical: https://daily.dev/posts/second-level-caching-in-ef-core-10-the-complete-guide-bvdnj7ehj
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Second-Level Caching in EF Core 10: The Complete Guide | daily.dev
og:description: 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...
og:url: https://daily.dev/posts/second-level-caching-in-ef-core-10-the-complete-guide-bvdnj7ehj
og:image: https://api.daily.dev/og/posts/bVdNJ7eHJ.png
og:image:alt: Second-Level Caching in EF Core 10: The Complete Guide
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Second-Level Caching in EF Core 10: The Complete Guide

**[Code with Mukesh](https://daily.dev/sources/codewithmukesh)** · [@iammukeshm](https://daily.dev/iammukeshm) · 18 min read · 0 upvotes · 0 comments

## Summary

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.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://codewithmukesh.com/blog/ef-core-second-level-caching>

## 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._

## Similar posts on daily.dev

- [EF Core Interceptors: The Complete Guide \(.NET 10\)](https://daily.dev/posts/ef-core-interceptors-the-complete-guide-net-10--u2nhhvfym) · Code with Mukesh · 15 upvotes · 1 comments
- [EF Core Performance Best Practices in .NET 10](https://daily.dev/posts/ef-core-performance-best-practices-in-net-10-syq7fm9cv) · We Are .NET · 55 upvotes · 4 comments
- [How to Fix Slow EF Core Queries: Practical Performance Tuning](https://daily.dev/posts/how-to-fix-slow-ef-core-queries-practical-performance-tuning-effrdqtga) · Syncfusion · 38 upvotes · 0 comments
- [Benchmarking HybridCache vs Redis in ASP.NET Core 11](https://daily.dev/posts/benchmarking-hybridcache-vs-redis-in-asp-net-core-11-c8rfx7ksa) · C\# Corner · 0 upvotes · 0 comments
- [Testing with EF Core in C\#: In-Memory vs SQLite for Unit Tests](https://daily.dev/posts/testing-with-ef-core-in-c-in-memory-vs-sqlite-for-unit-tests-j1c16jyvj) · We Are .NET · 1 upvotes · 0 comments

---

Tags: [#.net](https://daily.dev/tags/.net), [#redis](https://daily.dev/tags/redis)

[View this post on daily.dev](https://daily.dev/posts/second-level-caching-in-ef-core-10-the-complete-guide-bvdnj7ehj)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Second-Level Caching in EF Core 10: The Complete Guide","url":"https://daily.dev/posts/second-level-caching-in-ef-core-10-the-complete-guide-bvdnj7ehj","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/second-level-caching-in-ef-core-10-the-complete-guide-bvdnj7ehj"},"datePublished":"2026-07-20T15:27:07.027Z","dateModified":"2026-09-13T20:00:17.301Z","description":"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...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/814fda0fdf5592e6c5bfbcf1a6429aa7?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/814fda0fdf5592e6c5bfbcf1a6429aa7?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Code with Mukesh","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Person","name":"Mukesh Murugan","url":"https://daily.dev/iammukeshm","image":"https://lh3.googleusercontent.com/a-/AFdZucr2UBIpOeVTWohK8XKuXPOutNG6hgyQMz7CYj7LEA=s100","interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"EndorseAction"},"userInteractionCount":880}},"commentCount":0,"discussionUrl":"https://daily.dev/posts/second-level-caching-in-ef-core-10-the-complete-guide-bvdnj7ehj","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":".net,redis","timeRequired":"PT18M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Code with Mukesh","item":"https://daily.dev/sources/codewithmukesh"},{"@type":"ListItem","position":3,"name":"Second-Level Caching in EF Core 10: The Complete Guide"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/second-level-caching-in-ef-core-10-the-complete-guide-bvdnj7ehj#faq","mainEntity":[{"@type":"Question","name":"Does EF Core 10 have a built-in second-level cache?","acceptedAnswer":{"@type":"Answer","text":"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."}},{"@type":"Question","name":"Why does my EFCoreSecondLevelCacheInterceptor cache still return old data after an ExecuteUpdate call?","acceptedAnswer":{"@type":"Answer","text":"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."}},{"@type":"Question","name":"How much faster is a Redis or in-memory second-level cache hit compared to a normal EF Core database query?","acceptedAnswer":{"@type":"Answer","text":"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."}}]}
```

