{"id":21813561,"url":"https://github.com/vahidn/efcoresecondlevelcacheinterceptor","last_synced_at":"2025-05-13T21:10:02.735Z","repository":{"id":37898893,"uuid":"240666388","full_name":"VahidN/EFCoreSecondLevelCacheInterceptor","owner":"VahidN","description":"EF Core Second Level Cache Interceptor","archived":false,"fork":false,"pushed_at":"2025-04-06T17:52:17.000Z","size":2246,"stargazers_count":861,"open_issues_count":1,"forks_count":91,"subscribers_count":24,"default_branch":"master","last_synced_at":"2025-04-11T23:54:40.661Z","etag":null,"topics":["aspnetcore","ef-core","efcore","entity-framework","entity-framework-core","entityframework"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/VahidN.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":["https://www.coffeete.ir/vnasiri","https://www.buymeacoffee.com/vahidn"]}},"created_at":"2020-02-15T07:55:58.000Z","updated_at":"2025-04-11T16:46:31.000Z","dependencies_parsed_at":"2023-02-18T23:15:55.395Z","dependency_job_id":"43179106-c08e-41c7-97df-69f873d2341d","html_url":"https://github.com/VahidN/EFCoreSecondLevelCacheInterceptor","commit_stats":{"total_commits":168,"total_committers":8,"mean_commits":21.0,"dds":"0.23809523809523814","last_synced_commit":"e653eba005e094a1ed366968136d9b789fc9a429"},"previous_names":[],"tags_count":81,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VahidN%2FEFCoreSecondLevelCacheInterceptor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VahidN%2FEFCoreSecondLevelCacheInterceptor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VahidN%2FEFCoreSecondLevelCacheInterceptor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VahidN%2FEFCoreSecondLevelCacheInterceptor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/VahidN","download_url":"https://codeload.github.com/VahidN/EFCoreSecondLevelCacheInterceptor/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248497813,"owners_count":21113984,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aspnetcore","ef-core","efcore","entity-framework","entity-framework-core","entityframework"],"created_at":"2024-11-27T14:29:58.697Z","updated_at":"2025-04-28T15:26:48.014Z","avatar_url":"https://github.com/VahidN.png","language":"C#","readme":"# EF Core Second Level Cache Interceptor\n\n[![EFCoreSecondLevelCacheInterceptor](https://github.com/VahidN/EFCoreSecondLevelCacheInterceptor/workflows/.NET%20Core%20Build/badge.svg)](https://github.com/VahidN/EFCoreSecondLevelCacheInterceptor)\n\nSecond level caching is a query cache. The results of EF commands will be stored in the cache, so that the same EF\ncommands will retrieve their data from the cache rather than executing them against the database again.\n\n## How to upgrade to version 5\n\nTo support more advanced caching providers, this library uses different assemblies and NuGet packages now.\nTo upgrade to version 5, first remove the `EFCoreSecondLevelCacheInterceptor` dependency. It doesn't have any built-in\ncaching provider anymore.\nBut you can still use it to introduce your own custom caching provider by calling its\n`options.UseCustomCacheProvider\u003cT\u003e()` method (and you won't need the new packages).\nTo install `EFCoreSecondLevelCacheInterceptor` as before, run the following command in the Package Manager Console:\n\n[![Nuget](https://img.shields.io/nuget/v/EFCoreSecondLevelCacheInterceptor)](http://www.nuget.org/packages/EFCoreSecondLevelCacheInterceptor/)\n\n```powershell\ndotnet add package EFCoreSecondLevelCacheInterceptor\n```\n\nBut if you were using the built-in `In-Memory` cache provider, just install this new package:\n\n[![Nuget](https://img.shields.io/nuget/v/EFCoreSecondLevelCacheInterceptor.MemoryCache)](http://www.nuget.org/packages/EFCoreSecondLevelCacheInterceptor.MemoryCache/)\n\n```powershell\ndotnet add package EFCoreSecondLevelCacheInterceptor.MemoryCache\n```\n\nOr if you were using the `EasyCaching.Core provider`, install the new\n`EFCoreSecondLevelCacheInterceptor.EasyCaching.Core` package:\n\n[![Nuget](https://img.shields.io/nuget/v/EFCoreSecondLevelCacheInterceptor.EasyCaching.Core)](http://www.nuget.org/packages/EFCoreSecondLevelCacheInterceptor.EasyCaching.Core/)\n\n```powershell\ndotnet add package EFCoreSecondLevelCacheInterceptor.EasyCaching.Core\n```\n\nOr if you were using the `CacheManager.Core provider`, install the new\n`EFCoreSecondLevelCacheInterceptor.CacheManager.Core` package:\n\n[![Nuget](https://img.shields.io/nuget/v/EFCoreSecondLevelCacheInterceptor.CacheManager.Core)](http://www.nuget.org/packages/EFCoreSecondLevelCacheInterceptor.CacheManager.Core/)\n\n```powershell\ndotnet add package EFCoreSecondLevelCacheInterceptor.CacheManager.Core\n```\n\nAlso there are 3 new caching providers available in V5:\n\n### 1- EFCoreSecondLevelCacheInterceptor.StackExchange.Redis\n\nThis provider uses the StackExchange.Redis as a cache provider and it's preconfigured with a MessagePack serializer. To\nuse it, first you should install its new NuGet package:\n\n[![Nuget](https://img.shields.io/nuget/v/EFCoreSecondLevelCacheInterceptor.StackExchange.Redis)](http://www.nuget.org/packages/EFCoreSecondLevelCacheInterceptor.StackExchange.Redis/)\n\n```powershell\ndotnet add package EFCoreSecondLevelCacheInterceptor.StackExchange.Redis\n```\n\nAnd then you need to register its required services:\n\n```csharp\nvar redisOptions = new ConfigurationOptions\n                   {\n                     EndPoints = new EndPointCollection\n                     {\n                        {\n                            \"127.0.0.1\", 6379\n                        }\n                     },\n                     AllowAdmin = true,\n                     ConnectTimeout = 10000\n                   };\n\t\t\t\t   \nservices.AddEFSecondLevelCache(options\n                    =\u003e options.UseStackExchangeRedisCacheProvider(redisOptions, TimeSpan.FromMinutes(minutes: 5)));\n```\n\n### 2- EFCoreSecondLevelCacheInterceptor.FusionCache\n\nThis provider uses the [FusionCache](https://github.com/ZiggyCreatures/FusionCache) as a cache provider. To use it,\nfirst you should install its new NuGet package:\n\n[![Nuget](https://img.shields.io/nuget/v/EFCoreSecondLevelCacheInterceptor.FusionCache)](http://www.nuget.org/packages/EFCoreSecondLevelCacheInterceptor.FusionCache/)\n\n```powershell\ndotnet add package EFCoreSecondLevelCacheInterceptor.FusionCache\n```\n\nAnd then this is how you can register its required services:\n\n```csharp\nservices.AddFusionCache()\n            .WithOptions(options =\u003e\n            {\n                options.DefaultEntryOptions = new FusionCacheEntryOptions\n                {\n                    // CACHE DURATION\n                    Duration = TimeSpan.FromMinutes(minutes: 1),\n\n                    // FAIL-SAFE OPTIONS\n                    IsFailSafeEnabled = true,\n                    FailSafeMaxDuration = TimeSpan.FromHours(hours: 2),\n                    FailSafeThrottleDuration = TimeSpan.FromSeconds(seconds: 30),\n\n                    // FACTORY TIMEOUTS\n                    FactorySoftTimeout = TimeSpan.FromMilliseconds(milliseconds: 500),\n                    FactoryHardTimeout = TimeSpan.FromMilliseconds(milliseconds: 1500),\n\n                    // DISTRIBUTED CACHE\n                    DistributedCacheSoftTimeout = TimeSpan.FromSeconds(seconds: 10),\n                    DistributedCacheHardTimeout = TimeSpan.FromSeconds(seconds: 20),\n                    AllowBackgroundDistributedCacheOperations = true,\n\n                    // JITTERING\n                    JitterMaxDuration = TimeSpan.FromSeconds(seconds: 2)\n                };\n\n                // DISTIBUTED CACHE CIRCUIT-BREAKER\n                options.DistributedCacheCircuitBreakerDuration = TimeSpan.FromSeconds(seconds: 2);\n\n                // CUSTOM LOG LEVELS\n                options.FailSafeActivationLogLevel = LogLevel.Debug;\n                options.SerializationErrorsLogLevel = LogLevel.Warning;\n                options.DistributedCacheSyntheticTimeoutsLogLevel = LogLevel.Debug;\n                options.DistributedCacheErrorsLogLevel = LogLevel.Error;\n                options.FactorySyntheticTimeoutsLogLevel = LogLevel.Debug;\n                options.FactoryErrorsLogLevel = LogLevel.Error;\n            });\n\t\t\t\nservices.AddEFSecondLevelCache(options =\u003e options.UseFusionCacheProvider());\n```\n\n### 3- EFCoreSecondLevelCacheInterceptor.HybridCache\n\nThis provider uses the [HybridCache](https://learn.microsoft.com/en-us/aspnet/core/performance/caching/hybrid?view=aspnetcore-9.0) as a cache provider. To use it,\nfirst you should install its new NuGet package:\n\n[![Nuget](https://img.shields.io/nuget/v/EFCoreSecondLevelCacheInterceptor.HybridCache)](http://www.nuget.org/packages/EFCoreSecondLevelCacheInterceptor.HybridCache/)\n\n```powershell\ndotnet add package EFCoreSecondLevelCacheInterceptor.HybridCache\n```\n\nAnd then this is how you can register its required services:\n\n```csharp\t\t\t\nservices.AddEFSecondLevelCache(options =\u003e options.UseHybridCacheProvider());\n```\n\n\n## Usage ([1](#1--register-a-preferred-cache-provider) \u0026 [2](#2--add-secondlevelcacheinterceptor-to-your-dbcontextoptionsbuilder-pipeline) are mandatory)\n\n### 1- [Register a preferred cache provider](https://github.com/VahidN/EFCoreSecondLevelCacheInterceptor/tree/master/src/Tests/EFCoreSecondLevelCacheInterceptor.AspNetCoreSample/Startup.cs):\n\nYou can use the following cache providers:\n\n- [Memory provider built in this library](#using-the-built-in-in-memory-cache-provider)\n- [EasyCaching.Core memory provider](#using-easycachingcore-as-the-cache-provider)\n- [EasyCaching.Core dynamic cache provider](#using-easycachingcore-as-a-dynamic-cache-provider)\n- [CacheManager.Core cache provider](#using-cachemanagercore-as-the-cache-provider-its-not-actively-maintained)\n- [A custom cache provider](#using-a-custom-cache-provider)\n\n#### Using the built-in In-Memory cache provider\n\n![performance](https://raw.githubusercontent.com/VahidN/EFCoreSecondLevelCacheInterceptor/master/src/Tests/EFCoreSecondLevelCacheInterceptor.PerformanceTests/int-pref.png)\n\n```csharp\nnamespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample\n{\n    public class Startup\n    {\n        private readonly string _contentRootPath;\n\n        public Startup(IConfiguration configuration, IWebHostEnvironment env)\n        {\n            _contentRootPath = env.ContentRootPath;\n            Configuration = configuration;\n        }\n\n        public IConfiguration Configuration { get; }\n\n        public void ConfigureServices(IServiceCollection services)\n        {\n            services.AddEFSecondLevelCache(options =\u003e\n                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix(\"EF_\")\n                       // Fallback on db if the caching provider fails.\n                       .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1)))\n\n            // Please use the `CacheManager.Core` or `EasyCaching.Redis` for the Redis cache provider.\n            );\n\n            var connectionString = Configuration[\"ConnectionStrings:ApplicationDbContextConnection\"];\n            if (connectionString.Contains(\"%CONTENTROOTPATH%\"))\n            {\n                connectionString = connectionString.Replace(\"%CONTENTROOTPATH%\", _contentRootPath);\n            }\n            services.AddConfiguredMsSqlDbContext(connectionString);\n\n            services.AddControllersWithViews();\n        }\n    }\n}\n```\n\n#### Using EasyCaching.Core as the cache provider\n\nHere you can use the [EasyCaching.Core](https://github.com/dotnetcore/EasyCaching), as a highly configurable cache\nmanager too.\nTo use its in-memory caching mechanism, add this entry to the `.csproj` file:\n\n```xml\n  \u003cItemGroup\u003e\n    \u003cPackageReference Include=\"EasyCaching.InMemory\" Version=\"1.6.1\" /\u003e\n  \u003c/ItemGroup\u003e\n```\n\nThen register its required services:\n\n```csharp\nnamespace EFSecondLevelCache.Core.AspNetCoreSample\n{\n    public class Startup\n    {\n        public void ConfigureServices(IServiceCollection services)\n        {\n            const string providerName1 = \"InMemory1\";\n            services.AddEFSecondLevelCache(options =\u003e\n                    options.UseEasyCachingCoreProvider(providerName1, isHybridCache: false).ConfigureLogging(true).UseCacheKeyPrefix(\"EF_\")\n                           // Fallback on db if the caching provider fails.\n                           .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))\n            );\n\n            // Add an in-memory cache service provider\n            // More info: https://easycaching.readthedocs.io/en/latest/In-Memory/\n            services.AddEasyCaching(options =\u003e\n            {\n                // use memory cache with your own configuration\n                options.UseInMemory(config =\u003e\n                {\n                    config.DBConfig = new InMemoryCachingOptions\n                    {\n                        // scan time, default value is 60s\n                        ExpirationScanFrequency = 60,\n                        // total count of cache items, default value is 10000\n                        SizeLimit = 100,\n\n                        // enable deep clone when reading object from cache or not, default value is true.\n                        EnableReadDeepClone = false,\n                        // enable deep clone when writing object to cache or not, default value is false.\n                        EnableWriteDeepClone = false,\n                    };\n                    // the max random second will be added to cache's expiration, default value is 120\n                    config.MaxRdSecond = 120;\n                    // whether enable logging, default is false\n                    config.EnableLogging = false;\n                    // mutex key's alive time(ms), default is 5000\n                    config.LockMs = 5000;\n                    // when mutex key alive, it will sleep some time, default is 300\n                    config.SleepMs = 300;\n                }, providerName1);\n            });\n        }\n    }\n}\n```\n\nIf you want to use the Redis as the preferred cache provider with `EasyCaching.Core`, first install the following\npackage:\n\n```xml\n  \u003cItemGroup\u003e\n    \u003cPackageReference Include=\"EasyCaching.Redis\" Version=\"1.6.1\" /\u003e\n    \u003cPackageReference Include=\"EasyCaching.Serialization.MessagePack\" Version=\"1.6.1\" /\u003e\n  \u003c/ItemGroup\u003e\n```\n\nAnd then register its required services:\n\n```csharp\nnamespace EFSecondLevelCache.Core.AspNetCoreSample\n{\n    public class Startup\n    {\n        public void ConfigureServices(IServiceCollection services)\n        {\n            const string providerName1 = \"Redis1\";\n            services.AddEFSecondLevelCache(options =\u003e\n                    options.UseEasyCachingCoreProvider(providerName1, isHybridCache: false).ConfigureLogging(true).UseCacheKeyPrefix(\"EF_\")\n                           // Fallback on db if the caching provider fails (for example, if Redis is down).\n                           .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))\n            );\n\n            // More info: https://easycaching.readthedocs.io/en/latest/Redis/\n            services.AddEasyCaching(option =\u003e\n            {\n                option.UseRedis(config =\u003e\n                {\n                    config.DBConfig.AllowAdmin = true;\n                    config.DBConfig.SyncTimeout = 10000;\n                    config.DBConfig.AsyncTimeout = 10000;\n                    config.DBConfig.Endpoints.Add(new EasyCaching.Core.Configurations.ServerEndPoint(\"127.0.0.1\", 6379));\n                    config.EnableLogging = true;\n                    config.SerializerName = \"Pack\";\n                    config.DBConfig.ConnectionTimeout = 10000;\n                }, providerName1)\n                .WithMessagePack(so =\u003e\n                                      {\n                                         so.EnableCustomResolver = true;\n                                         so.CustomResolvers = CompositeResolver.Create(\n                                         new IMessagePackFormatter[]\n                                         {\n                                               DBNullFormatter.Instance, // This is necessary for the null values\n                                         },\n                                         new IFormatterResolver[]\n                                         {\n                                              NativeDateTimeResolver.Instance,\n                                              ContractlessStandardResolver.Instance,\n                                              StandardResolverAllowPrivate.Instance,\n                                         });\n                                       },\n                                       \"Pack\");\n            });\n        }\n    }\n}\n```\n\n[Here is a sample about it](https://github.com/VahidN/EFCoreSecondLevelCacheInterceptor/tree/master/src/Tests/Issues/Issue123WithMessagePack/EFServiceProvider.cs).\n\n#### Using EasyCaching.Core as a dynamic cache provider\n\nIf you want to support multitenancy in your application and have a different Redis database per each tenant, first\nregister multiple pre-configured providers with known `providerName`s and then select these `providerName`s based on the\ncurrent tenant this way dynamically:\n\n```C#\nservices.AddEFSecondLevelCache(options =\u003e\n    options.UseEasyCachingCoreProvider(\n\t   (serviceProvider, cacheKey) =\u003e \"redis-db-\" + serviceProvider.GetRequiredService\u003cIHttpContextAccesor\u003e().HttpContext.Request.Headers[\"tenant-id\"],\n\t   isHybridCache: false)\n\t// `Or` you can set the cache key prefix per tenant dynamically  \n\t.UseCacheKeyPrefix(serviceProvider =\u003e \"EF_\" + serviceProvider.GetRequiredService\u003cIHttpContextAccesor\u003e().HttpContext.Request.Headers[\"tenant-id\"])\n\t.ConfigureLogging(true)\n\t.UseCacheKeyPrefix(\"EF_\")\n        // Fallback on db if the caching provider fails.\n        .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))\n);\n```\n\n#### Using CacheManager.Core as the cache provider\n\nAlso here you can use the [CacheManager.Core](https://github.com/MichaCo/CacheManager), as a highly configurable cache\nmanager too.\nTo use its in-memory caching mechanism, add these entries to the `.csproj` file:\n\n```xml\n  \u003cItemGroup\u003e\n    \u003cPackageReference Include=\"CacheManager.Core\" Version=\"2.0.0\" /\u003e\n    \u003cPackageReference Include=\"CacheManager.Microsoft.Extensions.Caching.Memory\" Version=\"2.0.0\" /\u003e\n    \u003cPackageReference Include=\"CacheManager.Serialization.Json\" Version=\"2.0.0\" /\u003e\n  \u003c/ItemGroup\u003e\n```\n\nThen register its required services:\n\n```csharp\nnamespace EFSecondLevelCache.Core.AspNetCoreSample\n{\n    public class Startup\n    {\n        public void ConfigureServices(IServiceCollection services)\n        {\n            services.AddEFSecondLevelCache(options =\u003e\n                options.UseCacheManagerCoreProvider().ConfigureLogging(true).UseCacheKeyPrefix(\"EF_\")\n                       // Fallback on db if the caching provider fails.\n                       .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))\n            );\n\n            // Add an in-memory cache service provider\n            services.AddSingleton(typeof(ICacheManager\u003c\u003e), typeof(BaseCacheManager\u003c\u003e));\n            services.AddSingleton(typeof(ICacheManagerConfiguration),\n                new CacheManager.Core.CacheConfigurationBuilder()\n                        .WithJsonSerializer()\n                        .WithMicrosoftMemoryCacheHandle(instanceName: \"MemoryCache1\")\n                        .Build());\n        }\n    }\n}\n```\n\nIf you want to use the Redis as the preferred cache provider with `CacheManager.Core`, first install the\n`CacheManager.StackExchange.Redis` package and then register its required services:\n\n```csharp\n// Add Redis cache service provider\nvar jss = new JsonSerializerSettings\n{\n    NullValueHandling = NullValueHandling.Ignore,\n    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,\n    TypeNameHandling = TypeNameHandling.Auto,\n    Converters = { new SpecialTypesConverter() }\n};\n\nconst string redisConfigurationKey = \"redis\";\nservices.AddSingleton(typeof(ICacheManagerConfiguration),\n    new CacheManager.Core.CacheConfigurationBuilder()\n        .WithJsonSerializer(serializationSettings: jss, deserializationSettings: jss)\n        .WithUpdateMode(CacheUpdateMode.Up)\n        .WithRedisConfiguration(redisConfigurationKey, config =\u003e\n        {\n            config.WithAllowAdmin()\n                .WithDatabase(0)\n                .WithEndpoint(\"localhost\", 6379)\n                // Enables keyspace notifications to react on eviction/expiration of items.\n                // Make sure that all servers are configured correctly and 'notify-keyspace-events' is at least set to 'Exe', otherwise CacheManager will not retrieve any events.\n                // You can try 'Egx' or 'eA' value for the `notify-keyspace-events` too.\n                // See https://redis.io/topics/notifications#configuration for configuration details.\n                .EnableKeyspaceEvents();\n        })\n        .WithMaxRetries(100)\n        .WithRetryTimeout(50)\n        .WithRedisCacheHandle(redisConfigurationKey)\n        .Build());\nservices.AddSingleton(typeof(ICacheManager\u003c\u003e), typeof(BaseCacheManager\u003c\u003e));\n\nservices.AddEFSecondLevelCache(options =\u003e\n    options.UseCacheManagerCoreProvider().ConfigureLogging(true).UseCacheKeyPrefix(\"EF_\")\n           // Fallback on db if the caching provider fails.\n           .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))\n);\n```\n\n[Here is](https://github.com/VahidN/EFCoreSecondLevelCacheInterceptor/tree/master/src/Tests/EFCoreSecondLevelCacheInterceptor.Tests/Settings/EFServiceProvider.cs#L21)\nthe definition of the SpecialTypesConverter.\n\n#### Using a custom cache provider\n\nIf you don't want to use the above cache providers, implement your custom `IEFCacheServiceProvider` and then introduce\nit using the `options.UseCustomCacheProvider\u003cT\u003e()` method.\n\n### 2- [Add SecondLevelCacheInterceptor](https://github.com/VahidN/EFCoreSecondLevelCacheInterceptor/tree/master/src/Tests/EFCoreSecondLevelCacheInterceptor.Tests.DataLayer/MsSqlServiceCollectionExtensions.cs) to your\n`DbContextOptionsBuilder` pipeline:\n\n```csharp\n    public static class MsSqlServiceCollectionExtensions\n    {\n        public static IServiceCollection AddConfiguredMsSqlDbContext(this IServiceCollection services, string connectionString)\n        {\n            services.AddDbContextPool\u003cApplicationDbContext\u003e((serviceProvider, optionsBuilder) =\u003e\n                    optionsBuilder\n                        .UseSqlServer(\n                            connectionString,\n                            sqlServerOptionsBuilder =\u003e\n                            {\n                                sqlServerOptionsBuilder\n                                    .CommandTimeout((int)TimeSpan.FromMinutes(3).TotalSeconds)\n                                    .EnableRetryOnFailure()\n                                    .MigrationsAssembly(typeof(MsSqlServiceCollectionExtensions).Assembly.FullName);\n                            })\n                        .AddInterceptors(serviceProvider.GetRequiredService\u003cSecondLevelCacheInterceptor\u003e()));\n            return services;\n        }\n    }\n```\n\nNote: Some database providers don't support special fields such as `DateTimeOffset`, `TimeSpan`, etc. For these\nscenarios you will\nneed [the related converters](https://github.com/VahidN/EFCoreSecondLevelCacheInterceptor/blob/masterhttps://github.com/VahidN/EFCoreSecondLevelCacheInterceptor/tree/master/src/Tests/Issues/Issue9SQLiteInt32/DataLayer/ApplicationDbContext.cs#L35).\n\n### 3- Setting up the cache invalidation:\n\nThis library doesn't need any settings for the cache invalidation. It watches for all of the CRUD operations using its\ninterceptor and then invalidates the related cache entries automatically.\nBut if you want to invalidate the whole cache `manually`, inject the `IEFCacheServiceProvider` service and then call its\n`_cacheServiceProvider.ClearAllCachedEntries()` method or use it this way to specify the root cache keys which are a\ncollection of a Prefix+TableName:\n\n```C#\n// Partial cache invalidation using the specified table names\n// This is useful when you are monitoring your DB's changes using the SqlTableDependency\n_cacheServiceProvider.InvalidateCacheDependencies(new EFCacheKey(new HashSet\u003cstring\u003e()\n{\n   \"EF_TableName1\", // \"EF_\" is the cache key's prefix\n   \"EF_TableName2\"\n}));\n```\n\nI you want to get notified about the cache-invalidation events and involved cache dependencies, use the\n`NotifyCacheInvalidation` method:\n\n```C#\nservices.AddEFSecondLevelCache(options =\u003e\n{\n   options.UseMemoryCacheProvider(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(value: 30))\n                .NotifyCacheInvalidation(invalidationInfo =\u003e\n                {\n                    invalidationInfo.ServiceProvider.GetRequiredService\u003cILoggerFactory\u003e()\n                        .CreateLogger(categoryName: \"NotifyCacheInvalidation\")\n                        .LogWarning(message: \"{Message}\",\n                            invalidationInfo.ClearAllCachedEntries\n                                ? \"Invalidated all the cache entries!\"\n                                : $\"Invalidated [{string.Join(separator: \", \", invalidationInfo.CacheDependencies)}] dependencies.\");\n                })\n```\n\n### 4- To cache the results of the normal queries like:\n\n```csharp\nvar post1 = context.Posts\n                   .Where(x =\u003e x.Id \u003e 0)\n                   .OrderBy(x =\u003e x.Id)\n                   .FirstOrDefault();\n```\n\nWe can use the new `Cacheable()` extension method:\n\n```csharp\nvar post1 = context.Posts\n                   .Where(x =\u003e x.Id \u003e 0)\n                   .OrderBy(x =\u003e x.Id)\n                   .Cacheable(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(5))\n                   .FirstOrDefault();  // Async methods are supported too.\n```\n\nNOTE: It doesn't matter where the `Cacheable` method is located in this expression\ntree. [It just adds](https://github.com/VahidN/EFCoreSecondLevelCacheInterceptor/tree/master/src/EFCoreSecondLevelCacheInterceptor/EFCachedQueryExtensions.cs)\nthe standard `TagWith` method to mark this query as `Cacheable`. Later `SecondLevelCacheInterceptor` will use this tag\nto identify the `Cacheable` queries.\n\nAlso it's possible to set the `Cacheable()` method's settings globally:\n\n```csharp\nservices.AddEFSecondLevelCache(options =\u003e options.UseMemoryCacheProvider(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(5)).ConfigureLogging(true)\n                                          \t .UseCacheKeyPrefix(\"EF_\")\n                                                 // Fallback on db if the caching provider fails.\n                                                 .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))\n);\n```\n\nIn this case the above query will become:\n\n```csharp\nvar post1 = context.Posts\n                   .Where(x =\u003e x.Id \u003e 0)\n                   .OrderBy(x =\u003e x.Id)\n                   .Cacheable()\n                   .FirstOrDefault();  // Async methods are supported too.\n```\n\nIf you specify the settings of the `Cacheable()` method explicitly such as\n`Cacheable(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(5))`, its setting will override the global setting.\n\n## Caching all of the queries\n\nTo cache all of the system's queries, just set the `CacheAllQueries()` method:\n\n```csharp\nnamespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample\n{\n    public class Startup\n    {\n        public void ConfigureServices(IServiceCollection services)\n        {\n            services.AddEFSecondLevelCache(options =\u003e\n            {\n                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix(\"EF_\");\n                options.CacheAllQueries(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30));\n                // Fallback on db if the caching provider fails.\n                options.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1));\n            });\n\n            // ...\n```\n\nThis will put the whole system's queries in cache. In this case calling the `Cacheable()` methods won't be necessary. If\nyou specify the `Cacheable()` method, its setting will override this global setting. If you want to exclude some of the\nqueries from this global cache, apply the `NotCacheable()` method to them.\n\n## Caching some of the queries\n\nTo cache some of the system's queries based on their entity-types or table-names, use `CacheQueriesContainingTypes` or\n`CacheQueriesContainingTableNames` methods:\n\n```csharp\nnamespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample\n{\n    public class Startup\n    {\n        public void ConfigureServices(IServiceCollection services)\n        {\n            services.AddEFSecondLevelCache(options =\u003e\n            {\n                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix(\"EF_\")\n                       // Fallback on db if the caching provider fails.\n                       .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))\n                    /*.CacheQueriesContainingTypes(\n                        CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30), TableTypeComparison.Contains,\n                        typeof(Post), typeof(Product), typeof(User)\n                        )*/\n                    .CacheQueriesContainingTableNames(\n                        CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30), TableNameComparison.ContainsOnly,\n                        \"posts\", \"products\", \"users\"\n                        );\n            });\n\n            // ...\n```\n\nThis will put the the specified system's queries in cache. In this case calling the `Cacheable()` methods won't be\nnecessary. If you specify the `Cacheable()` method, its setting will override this global setting. If you want to\nexclude some of the queries from this global cache, apply the `NotCacheable()` method to them.\nAlso you can skip caching some of the defined DbContexts using `SkipCachingDbContexts()` method.\n\n## Skip caching of some of the queries\n\nTo skip caching some of the system's queries based on their SQL commands, set the `SkipCachingCommands` predicate:\n\n```csharp\nnamespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample\n{\n    public class Startup\n    {\n        public void ConfigureServices(IServiceCollection services)\n        {\n            services.AddEFSecondLevelCache(options =\u003e\n            {\n                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix(\"EF_\")\n                       // Fallback on db if the caching provider fails.\n                       .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))\n                        // How to skip caching specific commands\n                       .SkipCachingCommands(commandText =\u003e\n                                commandText.Contains(\"NEWID()\", StringComparison.InvariantCultureIgnoreCase));\n            });\n            // ...\n```\n\n## Skip caching of some of the queries based on their results\n\nTo skip caching some of the system's queries based on their results, set the `SkipCachingResults` predicate:\n\n```csharp\nnamespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample\n{\n    public class Startup\n    {\n        public void ConfigureServices(IServiceCollection services)\n        {\n            services.AddEFSecondLevelCache(options =\u003e\n            {\n                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix(\"EF_\")\n                        // Fallback on db if the caching provider fails.\n                        .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))\n                        // Don't cache null values. Remove this optional setting if it's not necessary.\n                        .SkipCachingResults(result =\u003e\n                                result.Value == null || (result.Value is EFTableRows rows \u0026\u0026 rows.RowsCount == 0));\n            });\n            // ...\n```\n\n## Skip caching some of the queries based on their table names\n\nTo do not cache some of the system's queries based on their entity-types or table-names, use\n`CacheAllQueriesExceptContainingTypes` or `CacheAllQueriesExceptContainingTableNames` methods:\n\n```csharp\nnamespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample\n{\n    public class Startup\n    {\n        public void ConfigureServices(IServiceCollection services)\n        {\n            services.AddEFSecondLevelCache(options =\u003e\n            {\n                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix(\"EF_\")\n                      // Fallback on db if the caching provider fails.\n                      .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))\n                    /*.CacheAllQueriesExceptContainingTypes(\n                        CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30),\n                        typeof(Post), typeof(Product), typeof(User)\n                        )*/\n                    .CacheAllQueriesExceptContainingTableNames(\n                        CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30),\n                        \"posts\", \"products\", \"users\"\n                        );\n            });\n\n            // ...\n```\n\nThis will not put the the specified system's queries in cache. In this case calling the `Cacheable()` methods won't be\nnecessary. If you specify the `Cacheable()` method, its setting will override this global setting.\n\n## Skip invalidating the related cache entries of a given query\n\nSometimes you don't want to invalidate the cache immediately, such when you are updating a post's likes or views count.\nIn this case to skip invalidating the related cache entries of a given CRUD command, set the\n`SkipCacheInvalidationCommands` predicate:\n\n```csharp\nnamespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample\n{\n    public class Startup\n    {\n        public void ConfigureServices(IServiceCollection services)\n        {\n            services.AddEFSecondLevelCache(options =\u003e\n            {\n                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix(\"EF_\")\n                    // Fallback on db if the caching provider fails.\n                    .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))\n                    .SkipCacheInvalidationCommands(commandText =\u003e\n                                // How to skip invalidating the related cache entries of this query\n                                commandText.Contains(\"NEWID()\", StringComparison.InvariantCultureIgnoreCase));\n            });\n            // ...\n```\n\n## Overriding the default cache-policy\n\nUse `OverrideCachePolicy()` method to override the default/calculated cache-policy of the currently executing query:\n\n```csharp\nnamespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample\n{\n    public class Startup\n    {\n        public void ConfigureServices(IServiceCollection services)\n        {\n            services.AddEFSecondLevelCache(options =\u003e\n            {\n                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix(\"EF_\")\n                    // Fallback on db if the caching provider fails.\n                    .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))\n                    .OverrideCachePolicy(context =\u003e\n\t\t\t\t\t{\n\t\t\t\t\t\tif (context.IsCrudCommand)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (context.CommandTableNames.Contains(item: \"posts\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn new EFCachePolicy().ExpirationMode(CacheExpirationMode.NeverRemove);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn null; // Use the default/calculated EFCachePolicy \n\t\t\t\t\t});\n            });\n            // ...\n```\n\n## Using a different hash provider\n\nThis library uses\nthe [XxHash64Unsafe](https://github.com/VahidN/EFCoreSecondLevelCacheInterceptor/tree/master/src/EFCoreSecondLevelCacheInterceptor/XxHash64Unsafe.cs)\nclass to calculate the hash of a query and its parameters to produce a corresponding cache-key.\n`xxHash` is an extremely fast `non-cryptographic` Hash algorithm. If you don't like it or you want to change it, just\nimplement the `IEFHashProvider` interface and then introduce it this way:\n\n```csharp\nnamespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample\n{\n    public class Startup\n    {\n        public void ConfigureServices(IServiceCollection services)\n        {\n            services.AddEFSecondLevelCache(options =\u003e\n            {\n                options.UseCustomHashProvider\u003cMyCustomHashProvider\u003e();\n                // ...                \n            });\n\n            // ...\n```\n\n## Disabling the interceptor for a while\n\nIf you want to disable this interceptor for a while, use the `.EnableCachingInterceptor(enable: false)` method. Its\ndefault value is true.\n\n## Providing options to control the serialization behavior\n\nThe EFCacheKeyProvider class serializes parameter values of a DbCommand to JSON values. If it causes an exception in\nsome cases, you can specify a custom JsonSerializerOptions for it using the `UseJsonSerializerOptions(options)` method.\n\n## Does it work?!\n\nYou should enable the logging system to see the behind the scene of the caching interceptor.\nFirst set the `ConfigureLogging(true)`:\n\n```c#\n services.AddEFSecondLevelCache(options =\u003e\n                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix(\"EF_\")\n                       // Fallback on db if the caching provider fails.\n                      .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))\n```\n\nAnd then change the log level to `Debug` in your `appsettings.json` file:\n\n```json\n{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Debug\",\n      \"System\": \"Debug\",\n      \"Microsoft\": \"Debug\",\n      \"Microsoft.Hosting.Lifetime\": \"Debug\"\n    }\n  }\n}\n```\n\nOr ... you can use the second optional parameter of the `ConfigureLogging` method to access the published events of this\nlibrary more easily:\n\n```c#\n .ConfigureLogging(enable: environment.IsDevelopment(), cacheableEvent: args =\u003e\n            {\n                    switch (args.EventId)\n                    {\n                        case CacheableLogEventId.CacheHit:\n                            break;\n                        case CacheableLogEventId.QueryResultCached:\n                            break;\n                        case CacheableLogEventId.QueryResultInvalidated:\n                            args.ServiceProvider.GetRequiredService\u003cILoggerFactory\u003e()\n                                .CreateLogger(nameof(EFCoreSecondLevelCacheInterceptor))\n                                .LogWarning(message: \"{EventId} -\u003e {Message} -\u003e {CommandText}\", args.EventId,\n                                    args.Message, args.CommandText);\n                            break;\n                        case CacheableLogEventId.CachingSkipped:\n                            break;\n                        case CacheableLogEventId.InvalidationSkipped:\n                            break;\n                        case CacheableLogEventId.CachingSystemStarted:\n                            break;\n                        case CacheableLogEventId.CachingError:\n                            break;\n                        case CacheableLogEventId.QueryResultSuppressed:\n                            break;\n                        case CacheableLogEventId.CacheDependenciesCalculated:\n                            break;\n                        case CacheableLogEventId.CachePolicyCalculated:\n                            break;\n                    }\n            })\n```\n\nNow after running a query multiple times, you should have these logged lines:\n\n```\nSuppressed result with a TableRows[ee20d2d7-ffc7-4ff9-9484-e8d4eecde53e] from the cache[KeyHash: EB153BD4, CacheDependencies: Page.].\nUsing the TableRows[ee20d2d7-ffc7-4ff9-9484-e8d4eecde53e] from the cache.\n```\n\nNotes:\n\n- Having the `Suppressed the result with the TableRows` message means the caching interceptor is working fine.\n- The next `Executed DbCommand` means nothing and it always will be logged by EF.\n- At the beginning there will be a lot of internal commands executed by the EF to run migrations, etc. Ignore these\n  commands, because you will see the `Suppressed the result with the TableRows` messages for the frequently running\n  queries.\n- Also you should verify it with a real DB profiler. It will log the 1st executed query and then on the 2nd run, you\n  won't see it anymore.\n\n## Samples\n\n- [Console App Sample](https://github.com/VahidN/EFCoreSecondLevelCacheInterceptor/tree/master/src/Tests/EFCoreSecondLevelCacheInterceptor.ConsoleSample/)\n- [ASP.NET Core App Sample](https://github.com/VahidN/EFCoreSecondLevelCacheInterceptor/tree/master/src/Tests/EFCoreSecondLevelCacheInterceptor.AspNetCoreSample/)\n- [Tests](https://github.com/VahidN/EFCoreSecondLevelCacheInterceptor/tree/master/src/Tests/EFCoreSecondLevelCacheInterceptor.Tests/)\n\n## Guidance\n\n### When to use\n\nGood candidates for query caching are global site settings and public data, such as infrequently changing articles or\ncomments. It can also be beneficial to cache data specific to a user so long as the cache expires frequently enough\nrelative to the size of the user base that memory consumption remains acceptable. Small, per-user data that frequently\nexceeds the cache's lifetime, such as a user's photo path, is better held in user claims, which are stored in cookies,\nthan in this cache.\n\n### Scope\n\nThis cache is scoped to the application, not the current user. It does not use session variables. Accordingly, when\nretrieving cached per-user data, be sure queries in include code such as `.Where(x =\u003e .... \u0026\u0026 x.UserId == id)`.\n\n### Invalidation\n\nThis cache is updated when an entity is changed (insert, update, or delete) via a DbContext that uses this library. If\nthe database is updated through some other means, such as a stored procedure or trigger, the cache becomes stale.\n\n### Transactions\n\nTo avoid complications, all of the queries inside an `explicit` transaction (context.Database.BeginTransaction()) will\nnot be cached. But the cache invalidations due to its CRUD operations will occur. You can use\n`.AllowCachingWithExplicitTransactions(true)` setting to disable it.\n","funding_links":["https://www.coffeete.ir/vnasiri","https://www.buymeacoffee.com/vahidn"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvahidn%2Fefcoresecondlevelcacheinterceptor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvahidn%2Fefcoresecondlevelcacheinterceptor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvahidn%2Fefcoresecondlevelcacheinterceptor/lists"}