Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
E

EFCoreSecondLevelCacheInterceptor

> 编程语言
Open source

EF Core Second Level Cache Interceptor

964 stars0 likes1 views
WebsiteGitHub

About

EF Core Second Level Cache Interceptor

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:

powershell
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.
    powershell
    dotnet add package EFCoreSecondLevelCacheInterceptor.MemoryCache
  • StackExchange.Redis: Uses Redis with a preconfigured MessagePack serializer.
    powershell
    dotnet add package EFCoreSecondLevelCacheInterceptor.StackExchange.Redis
  • FusionCache: Implements FusionCache as a cache provider.
    powershell
    dotnet add package EFCoreSecondLevelCacheInterceptor.FusionCache
  • HybridCache: Implements .NET's HybridCache.
    powershell
    dotnet add package EFCoreSecondLevelCacheInterceptor.HybridCache
  • EasyCaching.Core: A provider for the EasyCaching.Core library.
    powershell
    dotnet add package EFCoreSecondLevelCacheInterceptor.EasyCaching.Core
  • CacheManager.Core: A provider for the CacheManager.Core library.
    powershell
    dotnet add package EFCoreSecondLevelCacheInterceptor.CacheManager.Core
  • Custom: You can also implement your own provider.

Troubleshooting: Transitive Dependency Mitigation

If you encounter an issue with a transitive dependency before the upstream library author releases an update, you can force NuGet to use a patched version using the "Nearest Wins" rule. A direct package reference in your project file will always override deeper, transitive references.

To force the project to use a safe or updated version (e.g., MessagePack v3.1.7+), explicitly add it to your .csproj file:

xml

  

  

This workaround is perfectly safe for minor patches (e.g., 3.1.6 to 3.1.7) due to Semantic Versioning (SemVer) guarantees. However, forcing a major version jump (e.g., v2.x to v3.x) will likely introduce breaking changes and crash the parent library.

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

…

(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.

…

4. Make Queries Cacheable

To cache a query, use the .Cacheable() extension method. It can be placed anywhere in the LINQ query chain.

csharp
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:

csharp
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.

csharp
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.

json
{
  "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 TableRows message confirms the caching interceptor is working.
  • You will still see an Executed DbCommand log 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 ConfigureLogging method. 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().

csharp
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.

csharp
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 CacheAllQueriesExceptContainingTypes or CacheAllQueriesExceptContainingTableNames.

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:

    csharp
    _cacheServiceProvider.ClearAllCachedEntries();
  • Invalidate cache entries related to specific tables: This is useful if you are using an external tool like SqlTableDependency to monitor database changes.

    csharp
    // The prefix "EF_" should match your configured UseCacheKeyPrefix.
    var tableNames = new HashSet { "EF_TableName1", "EF_TableName2" };
    _cacheServiceProvider.InvalidateCacheDependencies(new EFCacheKey(tableNames));

Invalidation Notifications

To receive notifications when cache entries are invalidated, use the NotifyCacheInvalidation method.

…

Advanced Configuration

Skipping Caching

You can define rules to skip caching for certain queries based on their command text or results.

  • Skip by Command Text:
    csharp
    services.AddEFSecondLevelCache(options =>
    {
        options.SkipCachingCommands(commandText =>
            commandText.Contains("NEWID()", StringComparison.InvariantCultureIgnoreCase));
    });
  • Skip by Result:
    csharp
    services.AddEFSecondLevelCache(options =>
    {
        // Don't cache null values or empty result sets.
        options.SkipCachingResults(result =>
            result.Value == null || (result.Value is EFTableRows rows && rows.RowsCount == 0));
    });

Or if you don't want to use the NotCacheable() extension method, you can use query tags to attach comments to your SQL queries directly from your EF Core code. These tags are then included in the generated SQL, allowing you to create custom rules for caching behavior.

To add a query tag, use the .TagWith() extension method on your EF Core query:

csharp
var blogs = await context.Blogs
    .TagWith("Fetching data")
    .Where(b => b.IsActive)
    .ToListAsync();

The tag, prepended with --, will be included in the generated SQL query:

sql
-- Fetching data
SELECT * FROM Blogs WHERE IsActive = 1;

Now you can use query tags to define rules for when to skip caching. This is done by configuring your cache service to check for specific text in the command.

In your Startup.cs or Program.cs file, configure the cache to skip commands that contain your tag. The SkipCachingCommands method accepts a predicate that evaluates the command text.

csharp
services.AddEFSecondLevelCache(options =>
{
    options.SkipCachingCommands(commandText =>
        commandText.Contains("-- Fetching data", StringComparison.InvariantCultureIgnoreCase));
});

With this configuration, any query tagged with "Fetching data" will not be cached, giving you granular control over your caching strategy.

Skipping Invalidation

In some cases, you may want to prevent a command from invalidating the cache, such as when updating a view counter.

csharp
services.AddEFSecondLevelCache(options =>
{
    options.SkipCacheInvalidationCommands(commandText =>
        // Assumes a command that only updates a post's view count contains this text.
        commandText.Contains("UPDATE [Posts] SET [Views]", StringComparison.InvariantCultureIgnoreCase));
});

Overriding Cache Policy

Use OverrideCachePolicy() to dynamically change the caching policy for a query at runtime.

csharp
services.AddEFSecondLevelCache(options =>
{
    options.OverrideCachePolicy(context =>
    {
        // Don't cache any CRUD commands
        if (context.IsCrudCommand)
        {
            return null;
        }

        // Use a "never remove" policy for queries on the 'posts' table
        if (context.CommandTableNames.Contains("posts"))
        {
            return new EFCachePolicy().ExpirationMode(CacheExpirationMode.NeverRemove);
        }

        // Use the default calculated policy for all other queries
        return null;

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

C#aspnetcoreef-coreefcoreentity-framework

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言