EFCoreSecondLevelCacheInterceptor
EF Core Second Level Cache Interceptor
Install / Use
/learn @VahidN/EFCoreSecondLevelCacheInterceptorREADME
EF Core Second Level Cache Interceptor
Second-level caching is a query cache. The results of Entity Framework (EF) commands are stored in the cache so that the same EF commands will retrieve their data from the cache rather than executing them against the database again.
How to Use
Using the second-level cache involves three mandatory steps: installing a provider, registering it, and adding the interceptor to your DbContext.
1. Install a Preferred Cache Provider
First, you need to add the main package:
dotnet add package EFCoreSecondLevelCacheInterceptor
This library supports multiple caching providers, each available as a separate NuGet package. You must install at least one.
- In-Memory (Built-in): A simple in-memory cache provider.
dotnet add package EFCoreSecondLevelCacheInterceptor.MemoryCache - StackExchange.Redis: Uses Redis with a preconfigured MessagePack serializer.
dotnet add package EFCoreSecondLevelCacheInterceptor.StackExchange.Redis - FusionCache: Implements FusionCache as a cache provider.
dotnet add package EFCoreSecondLevelCacheInterceptor.FusionCache - HybridCache: Implements .NET's HybridCache.
dotnet add package EFCoreSecondLevelCacheInterceptor.HybridCache - EasyCaching.Core: A provider for the EasyCaching.Core library.
dotnet add package EFCoreSecondLevelCacheInterceptor.EasyCaching.Core - CacheManager.Core: A provider for the CacheManager.Core library.
dotnet add package EFCoreSecondLevelCacheInterceptor.CacheManager.Core - Custom: You can also implement your own provider.
2. Register the Cache Provider and Interceptor
In your Startup.cs or Program.cs, you need to register the EF Core second-level cache services and configure your chosen provider.
Example: Using the Built-in In-Memory Provider
public void ConfigureServices(IServiceCollection services)
{
// 1. Add EF Core Second Level Cache services
services.AddEFSecondLevelCache(options =>
options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix("EF_")
// Fallback on db if the caching provider fails.
.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
);
// 2. Add your DbContext
var connectionString = Configuration["ConnectionStrings:ApplicationDbContextConnection"];
services.AddConfiguredMsSqlDbContext(connectionString);
services.AddControllersWithViews();
}
(For detailed configuration examples of other providers, see the Available Cache Providers section below.)
3. Add the Interceptor to Your DbContext
Modify your DbContext registration to add the SecondLevelCacheInterceptor. This service is automatically registered and available via dependency injection.
public static class MsSqlServiceCollectionExtensions
{
public static IServiceCollection AddConfiguredMsSqlDbContext(this IServiceCollection services, string connectionString)
{
services.AddDbContextPool<ApplicationDbContext>((serviceProvider, optionsBuilder) =>
optionsBuilder
.UseSqlServer(
connectionString,
sqlServerOptionsBuilder =>
{
sqlServerOptionsBuilder
.CommandTimeout((int)TimeSpan.FromMinutes(3).TotalSeconds)
.EnableRetryOnFailure()
.MigrationsAssembly(typeof(MsSqlServiceCollectionExtensions).Assembly.FullName);
})
// Add the interceptor
.AddInterceptors(serviceProvider.GetRequiredService<SecondLevelCacheInterceptor>()));
return services;
}
}
4. Make Queries Cacheable
To cache a query, use the .Cacheable() extension method. It can be placed anywhere in the LINQ query chain.
var post = context.Posts
.Where(x => x.Id > 0)
.OrderBy(x => x.Id)
.Cacheable() // Mark this query to be cached
.FirstOrDefault(); // Async methods are also supported.
The Cacheable() method uses global settings by default, but you can override them for a specific query:
var post = context.Posts
.Where(x => x.Id > 0)
.OrderBy(x => x.Id)
// Override global settings for this query
.Cacheable(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(5))
.FirstOrDefault();
How to Verify It's Working
To confirm that the interceptor is working, you should enable logging.
1. Enable Logging in the Configuration
Set ConfigureLogging(true) during service registration.
services.AddEFSecondLevelCache(options =>
options.UseMemoryCacheProvider().ConfigureLogging(true)
);
2. Set Log Level to Debug
In your appsettings.json, ensure the log level is set to Debug.
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Debug",
"Microsoft": "Debug"
}
}
}
When you run a cacheable query for the first time, the data is fetched from the database and cached. On subsequent executions, you should see log messages indicating a cache hit:
Suppressed result with a TableRows[ee20d2d7-ffc7-4ff9-9484-e8d4eecde53e] from the cache[KeyHash: EB153BD4, CacheDependencies: Page.].
Using the TableRows[ee20d2d7-ffc7-4ff9-9484-e8d4eecde53e] from the cache.
Notes:
- The
Suppressed the result with the TableRowsmessage confirms the caching interceptor is working. - You will still see an
Executed DbCommandlog message from EF Core, but this is expected behavior. - You can also use a database profiler to verify that the query is only executed against the database on the first run.
- For more direct access to library events, you can pass an action to the
ConfigureLoggingmethod. See the Logging Events section for more details.
Caching Strategies
You can control caching globally or on a per-query basis.
Global Caching
Instead of marking individual queries with .Cacheable(), you can define a global caching policy.
Caching All Queries
To cache every query in the application, use CacheAllQueries().
services.AddEFSecondLevelCache(options =>
{
options.UseMemoryCacheProvider().UseCacheKeyPrefix("EF_");
options.CacheAllQueries(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30));
});
- If you use
CacheAllQueries(), you don't need to call.Cacheable()on individual queries. - A specific
.Cacheable()call on a query will override the global setting. - To exclude a specific query from global caching, use the
.NotCacheable()method.
Caching Queries by Type or Table Name
To cache queries that involve specific entity types or database tables, use CacheQueriesContainingTypes or CacheQueriesContainingTableNames.
services.AddEFSecondLevelCache(options =>
{
options.UseMemoryCacheProvider()
.CacheQueriesContainingTableNames(
CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30), TableNameComparison.ContainsOnly,
"posts", "products", "users"
);
});
- You can also exclude specific types or table names from caching using
CacheAllQueriesExceptContainingTypesorCacheAllQueriesExceptContainingTableNames.
Cache Invalidation
This library automatically invalidates cache entries when it detects CRUD operations (via its interceptor).
Automatic Invalidation
When you use SaveChanges() or SaveChangesAsync(), the interceptor identifies which tables have been modified and invalidates all cached queries that depend on those tables. No additional configuration is needed.
Limitation: ExecuteUpdate and ExecuteDelete
EF Core does not trigger interceptors for bulk operations like ExecuteUpdate and ExecuteDelete for performance reasons. These methods execute raw SQL directly, bypassing EF Core's change tracking and related events. Therefore, cache invalidation will not happen automatically for these operations. You must invalidate the cache manually.
Manual Invalidation
You can manually invalidate the cache by injecting the IEFCacheServiceProvider.
-
Invalidate the entire cache:
_cacheServiceProvider.ClearAllCachedEntries(); -
Invalidate cache entries related to specific tables: This is useful if you are using an external tool like
SqlTableDependencyto monitor database changes.// The prefix "EF_" should match your configured UseCacheKeyPrefix. var tableNames = new HashSet<string> { "EF_TableName1", "EF_TableName2" }; _cacheServiceProvider.InvalidateCacheDependencies(new EFCacheKey(tableNames));
Invalid
Related Skills
node-connect
339.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
339.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.8kCommit, push, and open a PR
