Giter Club home page Giter Club logo

efcoresecondlevelcacheinterceptor's Introduction

EF Core 3.1.x, 5x, 6x, 7x & 8x Second Level Cache Interceptor

EFCoreSecondLevelCacheInterceptor

Second level caching is a query cache. The results of EF commands will be 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.

Install via NuGet

To install EFCoreSecondLevelCacheInterceptor, run the following command in the Package Manager Console:

Nuget

PM> Install-Package EFCoreSecondLevelCacheInterceptor

You can also view the package page on NuGet.

Usage (1 & 2 are mandatory)

You can use the following cache providers:

Using the built-in In-Memory cache provider

performance

namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
{
    public class Startup
    {
        private readonly string _contentRootPath;

        public Startup(IConfiguration configuration, IWebHostEnvironment env)
        {
            _contentRootPath = env.ContentRootPath;
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddEFSecondLevelCache(options =>
                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix("EF_")
                       // Fallback on db if the caching provider fails.
                       .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))

            // Please use the `CacheManager.Core` or `EasyCaching.Redis` for the Redis cache provider.
            );

            var connectionString = Configuration["ConnectionStrings:ApplicationDbContextConnection"];
            if (connectionString.Contains("%CONTENTROOTPATH%"))
            {
                connectionString = connectionString.Replace("%CONTENTROOTPATH%", _contentRootPath);
            }
            services.AddConfiguredMsSqlDbContext(connectionString);

            services.AddControllersWithViews();
        }
    }
}

Using EasyCaching.Core as the cache provider

Here you can use the EasyCaching.Core, as a highly configurable cache manager too. To use its in-memory caching mechanism, add this entry to the .csproj file:

  <ItemGroup>
    <PackageReference Include="EasyCaching.InMemory" Version="1.6.1" />
  </ItemGroup>

Then register its required services:

namespace EFSecondLevelCache.Core.AspNetCoreSample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            const string providerName1 = "InMemory1";
            services.AddEFSecondLevelCache(options =>
                    options.UseEasyCachingCoreProvider(providerName1, isHybridCache: false).ConfigureLogging(true).UseCacheKeyPrefix("EF_")
                           // Fallback on db if the caching provider fails.
                           .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
            );

            // Add an in-memory cache service provider
            // More info: https://easycaching.readthedocs.io/en/latest/In-Memory/
            services.AddEasyCaching(options =>
            {
                // use memory cache with your own configuration
                options.UseInMemory(config =>
                {
                    config.DBConfig = new InMemoryCachingOptions
                    {
                        // scan time, default value is 60s
                        ExpirationScanFrequency = 60,
                        // total count of cache items, default value is 10000
                        SizeLimit = 100,

                        // enable deep clone when reading object from cache or not, default value is true.
                        EnableReadDeepClone = false,
                        // enable deep clone when writing object to cache or not, default value is false.
                        EnableWriteDeepClone = false,
                    };
                    // the max random second will be added to cache's expiration, default value is 120
                    config.MaxRdSecond = 120;
                    // whether enable logging, default is false
                    config.EnableLogging = false;
                    // mutex key's alive time(ms), default is 5000
                    config.LockMs = 5000;
                    // when mutex key alive, it will sleep some time, default is 300
                    config.SleepMs = 300;
                }, providerName1);
            });
        }
    }
}

If you want to use the Redis as the preferred cache provider with EasyCaching.Core, first install the following package:

  <ItemGroup>
    <PackageReference Include="EasyCaching.Redis" Version="1.6.1" />
    <PackageReference Include="EasyCaching.Serialization.MessagePack" Version="1.6.1" />
  </ItemGroup>

And then register its required services:

namespace EFSecondLevelCache.Core.AspNetCoreSample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            const string providerName1 = "Redis1";
            services.AddEFSecondLevelCache(options =>
                    options.UseEasyCachingCoreProvider(providerName1, isHybridCache: false).ConfigureLogging(true).UseCacheKeyPrefix("EF_")
                           // Fallback on db if the caching provider fails (for example, if Redis is down).
                           .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
            );

            // More info: https://easycaching.readthedocs.io/en/latest/Redis/
            services.AddEasyCaching(option =>
            {
                option.UseRedis(config =>
                {
                    config.DBConfig.AllowAdmin = true;
                    config.DBConfig.SyncTimeout = 10000;
                    config.DBConfig.AsyncTimeout = 10000;
                    config.DBConfig.Endpoints.Add(new EasyCaching.Core.Configurations.ServerEndPoint("127.0.0.1", 6379));
                    config.EnableLogging = true;
                    config.SerializerName = "Pack";
                    config.DBConfig.ConnectionTimeout = 10000;
                }, providerName1)
                .WithMessagePack(so =>
                                      {
                                         so.EnableCustomResolver = true;
                                         so.CustomResolvers = CompositeResolver.Create(
                                         new IMessagePackFormatter[]
                                         {
                                               DBNullFormatter.Instance, // This is necessary for the null values
                                         },
                                         new IFormatterResolver[]
                                         {
                                              NativeDateTimeResolver.Instance,
                                              ContractlessStandardResolver.Instance,
                                              StandardResolverAllowPrivate.Instance,
                                         });
                                       },
                                       "Pack");
            });
        }
    }
}

Here is a sample about it.

Using EasyCaching.Core as a dynamic cache provider

If you want to support multitenancy in your application and have a different Redis database per each tenant, first register multiple pre-configured providers with known providerNames and then select these providerNames based on the current tenant this way dynamically:

services.AddEFSecondLevelCache(options =>
    options.UseEasyCachingCoreProvider(
	   (serviceProvider, cacheKey) => "redis-db-" + serviceProvider.GetRequiredService<IHttpContextAccesor>().HttpContext.Request.Headers["tenant-id"],
	   isHybridCache: false)
	// `Or` you can set the cache key prefix per tenant dynamically  
	.UseCacheKeyPrefix(serviceProvider => "EF_" + serviceProvider.GetRequiredService<IHttpContextAccesor>().HttpContext.Request.Headers["tenant-id"])
	.ConfigureLogging(true)
	.UseCacheKeyPrefix("EF_")
        // Fallback on db if the caching provider fails.
        .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
);

Using CacheManager.Core as the cache provider [It's not actively maintained]

Also here you can use the CacheManager.Core, as a highly configurable cache manager too. To use its in-memory caching mechanism, add these entries to the .csproj file:

  <ItemGroup>
    <PackageReference Include="CacheManager.Core" Version="1.2.0" />
    <PackageReference Include="CacheManager.Microsoft.Extensions.Caching.Memory" Version="1.2.0" />
    <PackageReference Include="CacheManager.Serialization.Json" Version="1.2.0" />
  </ItemGroup>

Then register its required services:

namespace EFSecondLevelCache.Core.AspNetCoreSample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddEFSecondLevelCache(options =>
                options.UseCacheManagerCoreProvider().ConfigureLogging(true).UseCacheKeyPrefix("EF_")
                       // Fallback on db if the caching provider fails.
                       .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
            );

            // Add an in-memory cache service provider
            services.AddSingleton(typeof(ICacheManager<>), typeof(BaseCacheManager<>));
            services.AddSingleton(typeof(ICacheManagerConfiguration),
                new CacheManager.Core.ConfigurationBuilder()
                        .WithJsonSerializer()
                        .WithMicrosoftMemoryCacheHandle(instanceName: "MemoryCache1")
                        .Build());
        }
    }
}

If you want to use the Redis as the preferred cache provider with CacheManager.Core, first install the CacheManager.StackExchange.Redis package and then register its required services:

// Add Redis cache service provider
var jss = new JsonSerializerSettings
{
    NullValueHandling = NullValueHandling.Ignore,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
    TypeNameHandling = TypeNameHandling.Auto,
    Converters = { new SpecialTypesConverter() }
};

const string redisConfigurationKey = "redis";
services.AddSingleton(typeof(ICacheManagerConfiguration),
    new CacheManager.Core.ConfigurationBuilder()
        .WithJsonSerializer(serializationSettings: jss, deserializationSettings: jss)
        .WithUpdateMode(CacheUpdateMode.Up)
        .WithRedisConfiguration(redisConfigurationKey, config =>
        {
            config.WithAllowAdmin()
                .WithDatabase(0)
                .WithEndpoint("localhost", 6379)
                // Enables keyspace notifications to react on eviction/expiration of items.
                // 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.
                // You can try 'Egx' or 'eA' value for the `notify-keyspace-events` too.
                // See https://redis.io/topics/notifications#configuration for configuration details.
                .EnableKeyspaceEvents();
        })
        .WithMaxRetries(100)
        .WithRetryTimeout(50)
        .WithRedisCacheHandle(redisConfigurationKey)
        .Build());
services.AddSingleton(typeof(ICacheManager<>), typeof(BaseCacheManager<>));

services.AddEFSecondLevelCache(options =>
    options.UseCacheManagerCoreProvider().ConfigureLogging(true).UseCacheKeyPrefix("EF_")
           // Fallback on db if the caching provider fails.
           .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
);

Here is the definition of the SpecialTypesConverter.

Using a custom cache provider

If you don't want to use the above cache providers, implement your custom IEFCacheServiceProvider and then introduce it using the options.UseCustomCacheProvider<T>() method.

2- Add SecondLevelCacheInterceptor to your DbContextOptionsBuilder pipeline:

    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);
                            })
                        .AddInterceptors(serviceProvider.GetRequiredService<SecondLevelCacheInterceptor>()));
            return services;
        }
    }

Note: Some database providers don't support special fields such as DateTimeOffset, TimeSpan, etc. For these scenarios you will need the related converters.

3- Setting up the cache invalidation:

This library doesn't need any settings for the cache invalidation. It watches for all of the CRUD operations using its interceptor and then invalidates the related cache entries automatically. But if you want to invalidate the whole cache manually, inject the IEFCacheServiceProvider service and then call its _cacheServiceProvider.ClearAllCachedEntries() method or use it this way to specify the root cache keys which are a collection of a Prefix+TableName:

// Partial cache invalidation using the specified table names
// This is useful when you are monitoring your DB's changes using the SqlTableDependency
_cacheServiceProvider.InvalidateCacheDependencies(new EFCacheKey(new HashSet<string>()
{
   "EF_TableName1", // "EF_" is the cache key's prefix
   "EF_TableName2"
} {  KeyHash = "empty" }));

4- To cache the results of the normal queries like:

var post1 = context.Posts
                   .Where(x => x.Id > 0)
                   .OrderBy(x => x.Id)
                   .FirstOrDefault();

We can use the new Cacheable() extension method:

var post1 = context.Posts
                   .Where(x => x.Id > 0)
                   .OrderBy(x => x.Id)
                   .Cacheable(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(5))
                   .FirstOrDefault();  // Async methods are supported too.

NOTE: It doesn't matter where the Cacheable method is located in this expression tree. It just adds the standard TagWith method to mark this query as Cacheable. Later SecondLevelCacheInterceptor will use this tag to identify the Cacheable queries.

Also it's possible to set the Cacheable() method's settings globally:

services.AddEFSecondLevelCache(options => options.UseMemoryCacheProvider(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(5)).ConfigureLogging(true)
                                          	 .UseCacheKeyPrefix("EF_")
                                                 // Fallback on db if the caching provider fails.
                                                 .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
);

In this case the above query will become:

var post1 = context.Posts
                   .Where(x => x.Id > 0)
                   .OrderBy(x => x.Id)
                   .Cacheable()
                   .FirstOrDefault();  // Async methods are supported too.

If you specify the settings of the Cacheable() method explicitly such as Cacheable(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(5)), its setting will override the global setting.

Caching all of the queries

To cache all of the system's queries, just set the CacheAllQueries() method:

namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddEFSecondLevelCache(options =>
            {
                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix("EF_");
                options.CacheAllQueries(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30));
                // Fallback on db if the caching provider fails.
                options.UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1));
            });

            // ...

This will put the whole system's queries in cache. In this case calling the Cacheable() methods won't be necessary. If you specify the Cacheable() method, its setting will override this global setting. If you want to exclude some of the queries from this global cache, apply the NotCacheable() method to them.

Caching some of the queries

To cache some of the system's queries based on their entity-types or table-names, use CacheQueriesContainingTypes or CacheQueriesContainingTableNames methods:

namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddEFSecondLevelCache(options =>
            {
                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix("EF_")
                       // Fallback on db if the caching provider fails.
                       .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
                    /*.CacheQueriesContainingTypes(
                        CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30), TableTypeComparison.Contains,
                        typeof(Post), typeof(Product), typeof(User)
                        )*/
                    .CacheQueriesContainingTableNames(
                        CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30), TableNameComparison.ContainsOnly,
                        "posts", "products", "users"
                        );
            });

            // ...

This will put the the specified system's queries in cache. In this case calling the Cacheable() methods won't be necessary. If you specify the Cacheable() method, its setting will override this global setting. If you want to exclude some of the queries from this global cache, apply the NotCacheable() method to them.

Skip caching of some of the queries

To skip caching some of the system's queries based on their SQL commands, set the SkipCachingCommands predicate:

namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddEFSecondLevelCache(options =>
            {
                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix("EF_")
                       // Fallback on db if the caching provider fails.
                       .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
                        // How to skip caching specific commands
                       .SkipCachingCommands(commandText =>
                                commandText.Contains("NEWID()", StringComparison.InvariantCultureIgnoreCase));
            });
            // ...

Skip caching of some of the queries based on their results

To skip caching some of the system's queries based on their results, set the SkipCachingResults predicate:

namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddEFSecondLevelCache(options =>
            {
                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix("EF_")
                        // Fallback on db if the caching provider fails.
                        .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
                        // Don't cache null values. Remove this optional setting if it's not necessary.
                        .SkipCachingResults(result =>
                                result.Value == null || (result.Value is EFTableRows rows && rows.RowsCount == 0));
            });
            // ...

Skip caching some of the queries based on their table names

To do not cache some of the system's queries based on their entity-types or table-names, use CacheAllQueriesExceptContainingTypes or CacheAllQueriesExceptContainingTableNames methods:

namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddEFSecondLevelCache(options =>
            {
                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix("EF_")
                      // Fallback on db if the caching provider fails.
                      .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
                    /*.CacheAllQueriesExceptContainingTypes(
                        CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30),
                        typeof(Post), typeof(Product), typeof(User)
                        )*/
                    .CacheAllQueriesExceptContainingTableNames(
                        CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30),
                        "posts", "products", "users"
                        );
            });

            // ...

This will not put the the specified system's queries in cache. In this case calling the Cacheable() methods won't be necessary. If you specify the Cacheable() method, its setting will override this global setting.

Skip invalidating the related cache entries of a given query

Sometimes you don't want to invalidate the cache immediately, such when you are updating a post's likes or views count. In this case to skip invalidating the related cache entries of a given CRUD command, set the SkipCacheInvalidationCommands predicate:

namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddEFSecondLevelCache(options =>
            {
                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix("EF_")
                    // Fallback on db if the caching provider fails.
                    .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))
                    .SkipCacheInvalidationCommands(commandText =>
                                // How to skip invalidating the related cache entries of this query
                                commandText.Contains("NEWID()", StringComparison.InvariantCultureIgnoreCase));
            });
            // ...

Using a different hash provider

This library uses the XxHash64Unsafe class to calculate the hash of a query and its parameters to produce a corresponding cache-key. xxHash is an extremely fast non-cryptographic Hash algorithm. If you don't like it or you want to change it, just implement the IEFHashProvider interface and then introduce it this way:

namespace EFCoreSecondLevelCacheInterceptor.AspNetCoreSample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddEFSecondLevelCache(options =>
            {
                options.UseCustomHashProvider<MyCustomHashProvider>();
                // ...                
            });

            // ...

Disabling the interceptor for a while

If you want to disable this interceptor for a while, use the .EnableCachingInterceptor(enable: false) method. Its default value is true.

Does it work?!

You should enable the logging system to see the behind the scene of the caching interceptor. First set the ConfigureLogging(true):

 services.AddEFSecondLevelCache(options =>
                options.UseMemoryCacheProvider().ConfigureLogging(true).UseCacheKeyPrefix("EF_")
                       // Fallback on db if the caching provider fails.
                      .UseDbCallsIfCachingProviderIsDown(TimeSpan.FromMinutes(1))

And then change the log level to Debug in your appsettings.json file:

{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "System": "Debug",
      "Microsoft": "Debug",
      "Microsoft.Hosting.Lifetime": "Debug"
    }
  }
}

Or ... you can use the second optional parameter of the ConfigureLogging method to access the published events of this library more easily:

 .ConfigureLogging(enable: environment.IsDevelopment(), cacheableEvent: args =>
            {
                    switch (args.EventId)
                    {
                        case CacheableLogEventId.CacheHit:
                            break;
                        case CacheableLogEventId.QueryResultCached:
                            break;
                        case CacheableLogEventId.QueryResultInvalidated:
                            Console.ForegroundColor = ConsoleColor.Green;
                            Console.WriteLine($"{Environment.NewLine}{args.EventId} -> {args.Message}");

                            break;
                        case CacheableLogEventId.CachingSkipped:
                            break;
                        case CacheableLogEventId.InvalidationSkipped:
                            break;
                        case CacheableLogEventId.CachingSystemStarted:
                            break;
                        case CacheableLogEventId.CachingError:
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine($"{Environment.NewLine}{args.EventId} -> {args.Message}");

                            break;
                        case CacheableLogEventId.QueryResultSuppressed:
                            break;
                        case CacheableLogEventId.CacheDependenciesCalculated:
                            break;
                        case CacheableLogEventId.CachePolicyCalculated:
                            break;
                    }

                    Console.ResetColor();
            })

Now after running a query multiple times, you should have these logged lines:

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:

  • Having the Suppressed the result with the TableRows message means the caching interceptor is working fine.
  • The next Executed DbCommand means nothing and it always will be logged by EF.
  • At the beginning there will be a lot of internal commands executed by the EF to run migrations, etc. Ignore these commands, because you will see the Suppressed the result with the TableRows messages for the frequently running queries.
  • Also you should verify it with a real DB profiler. It will log the 1st executed query and then on the 2nd run, you won't see it anymore.

Samples

Guidance

When to use

Good candidates for query caching are global site settings and public data, such as infrequently changing articles or comments. It can also be beneficial to cache data specific to a user so long as the cache expires frequently enough relative to the size of the user base that memory consumption remains acceptable. Small, per-user data that frequently exceeds the cache's lifetime, such as a user's photo path, is better held in user claims, which are stored in cookies, than in this cache.

Scope

This cache is scoped to the application, not the current user. It does not use session variables. Accordingly, when retrieving cached per-user data, be sure queries in include code such as .Where(x => .... && x.UserId == id).

Invalidation

This cache is updated when an entity is changed (insert, update, or delete) via a DbContext that uses this library. If the database is updated through some other means, such as a stored procedure or trigger, the cache becomes stale.

Transactions

To avoid complications, all of the queries inside an explicit transaction (context.Database.BeginTransaction()) will not be cached. But the cache invalidations due to its CRUD operations will occur. You can use .AllowCachingWithExplicitTransactions(true) setting to disable it.

efcoresecondlevelcacheinterceptor's People

Contributors

dependabot[bot] avatar dikklop avatar markciliavincenti avatar petermorlion avatar ryanthomas73 avatar thatdeveloper avatar vahidn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

efcoresecondlevelcacheinterceptor's Issues

Cachable Queries always hiting the DB. "[KeyHash: xxxx, CacheDependencies: xxx.] Was not present in the cache."

Summary of the issue

The cache seems to be configured and working (no exceptions or errors and is logging fine) but every time I Reload a page the DB gets hit, If I read the cache log it keeps saying "[Entity xxxx] was not present in the cache.", I reload the same page multiple times and every time i get the same message, then next "added to the cache" but as soon as I reload "was not present in the cache" again and the DB gets hit.

I am not sure this is an issue, more than likely I am doing something wrong or missing a configuration or code line, but after trying multiple things I gave up and any help will be greatly appreciated.

Environment

.NET Core SDK version: 3.1
Microsoft.EntityFrameworkCore version: 3.1.2
EFCoreSecondLevelCacheInterceptor version: 1.5.4

And this is my Startup configuration:

public void ConfigureServices(IServiceCollection services)
        {
           services.AddEFSecondLevelCache(options =>
        options.UseMemoryCacheProvider(CacheExpirationMode.Absolute,TimeSpan.FromMinutes(30))
                       .DisableLogging(false)
                       .CacheAllQueries(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30))
            );

            services.AddControllersWithViews().AddRazorRuntimeCompilation();
            services.AddOptions();
            services.Configure<AppConfig>(Configuration.GetSection("AppConfig"));



            services.AddDbContext<PetraCMSDbContext>(options =>                                               
                          options.UseSqlServer(Configuration.GetConnectionString("PetraCMSDbContext"))
                          .AddInterceptors(new SecondLevelCacheInterceptor())
             );
}

I also tried adding ".Cachable()" to some of the queries directly despite having configured the cache to "CacheAllQueries", but with no success:

        public SettingsDto GetSettings()
        {
            Settings settings = _dbContext.Settings.Cacheable().FirstOrDefault();
            if (settings != null)
                return settings.ToSettingsDtoMap();
            return null;
        }

Output:

There are no exceptions or errors, Just that the DB is Hit every single time (tested this with a trace to my local DB) and the EFSecondLevelCacheInterceptor does not seem to find anything on the cache, keeps returning "was not present in the cache".

This is what I get on the logs:

Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
[KeyHash: 8C5814DB, CacheDependencies: User.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
TableRows[eecca79c-18b4-4972-b685-04e7a3df5843] added to the cache[KeyHash: 8C5814DB, CacheDependencies: User.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E2FAFD43, CacheDependencies: Page.
[KeyHash: E2FAFD43, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E2FAFD43, CacheDependencies: Page.
TableRows[274d4289-8ea0-4399-9f15-52e27fe01694] added to the cache[KeyHash: E2FAFD43, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: A4B849A, CacheDependencies: Page.
[KeyHash: A4B849A, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: A4B849A, CacheDependencies: Page.
TableRows[1f314a3c-67b8-4a95-b4ba-815cdc904981] added to the cache[KeyHash: A4B849A, CacheDependencies: Page.].
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
Invalidated [EFUnknownsCacheDependency, Page] dependencies.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
[KeyHash: 8C5814DB, CacheDependencies: User.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
TableRows[df62fdb3-43a7-42f5-a415-b48743dceee2] added to the cache[KeyHash: 8C5814DB, CacheDependencies: User.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E8A0662F, CacheDependencies: Page.
[KeyHash: E8A0662F, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E8A0662F, CacheDependencies: Page.
TableRows[910c07c3-37cc-4345-bb25-835e56401237] added to the cache[KeyHash: E8A0662F, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: EB153BD4, CacheDependencies: Page.
[KeyHash: EB153BD4, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: EB153BD4, CacheDependencies: Page.
TableRows[ee20d2d7-ffc7-4ff9-9484-e8d4eecde53e] added to the cache[KeyHash: EB153BD4, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E2FAFD43, CacheDependencies: Page.
[KeyHash: E2FAFD43, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E2FAFD43, CacheDependencies: Page.
TableRows[66d7b258-bad2-46b1-b5b7-c65132f8e47c] added to the cache[KeyHash: E2FAFD43, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: EB153BD4, CacheDependencies: Page.
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.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E2FAFD43, CacheDependencies: Page.
Suppressed result with a TableRows[66d7b258-bad2-46b1-b5b7-c65132f8e47c] from the cache[KeyHash: E2FAFD43, CacheDependencies: Page.].
Using the TableRows[66d7b258-bad2-46b1-b5b7-c65132f8e47c] from the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
[KeyHash: 8C5814DB, CacheDependencies: User.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
TableRows[5fb2e0e0-6dfe-440b-8799-4590f1ed8922] added to the cache[KeyHash: 8C5814DB, CacheDependencies: User.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
[KeyHash: BC1A4D8, CacheDependencies: Settings.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
TableRows[dea139ee-ef27-47fc-9e08-33e6c685b60a] added to the cache[KeyHash: BC1A4D8, CacheDependencies: Settings.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
[KeyHash: 8C5814DB, CacheDependencies: User.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
TableRows[86b744a1-faab-4e63-9cc7-960fa5c26673] added to the cache[KeyHash: 8C5814DB, CacheDependencies: User.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
[KeyHash: BC1A4D8, CacheDependencies: Settings.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
TableRows[0dc93235-7755-4e8a-b082-67af9e5a3fb2] added to the cache[KeyHash: BC1A4D8, CacheDependencies: Settings.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
[KeyHash: 8C5814DB, CacheDependencies: User.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
TableRows[81f84c59-1280-4ca2-91b1-59b226c8b8a9] added to the cache[KeyHash: 8C5814DB, CacheDependencies: User.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
[KeyHash: BC1A4D8, CacheDependencies: Settings.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
TableRows[857a6ecf-22a1-4261-b2aa-2e81cc56dd65] added to the cache[KeyHash: BC1A4D8, CacheDependencies: Settings.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
[KeyHash: 8C5814DB, CacheDependencies: User.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
TableRows[c60ee330-fd23-49df-8846-8e1cbf3ba658] added to the cache[KeyHash: 8C5814DB, CacheDependencies: User.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E2FAFD43, CacheDependencies: Page.
[KeyHash: E2FAFD43, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E2FAFD43, CacheDependencies: Page.
TableRows[67293f94-2a0b-4da8-b285-ba4cfbdb5f9b] added to the cache[KeyHash: E2FAFD43, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: EB153BD4, CacheDependencies: Page.
[KeyHash: EB153BD4, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: EB153BD4, CacheDependencies: Page.
TableRows[334d4083-3683-4cba-9a55-1115fed9227c] added to the cache[KeyHash: EB153BD4, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
[KeyHash: 8C5814DB, CacheDependencies: User.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
TableRows[169eb286-9726-435e-8834-e46491df4ee5] added to the cache[KeyHash: 8C5814DB, CacheDependencies: User.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: EB153BD4, CacheDependencies: Page.
[KeyHash: EB153BD4, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: EB153BD4, CacheDependencies: Page.
TableRows[e01343cc-731e-4d14-b81c-eaf37087d094] added to the cache[KeyHash: EB153BD4, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E2FAFD43, CacheDependencies: Page.
[KeyHash: E2FAFD43, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E2FAFD43, CacheDependencies: Page.
TableRows[846b0cb8-fb0a-4ea7-86c5-4b903de0a7bc] added to the cache[KeyHash: E2FAFD43, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: A4B849A, CacheDependencies: Page.
[KeyHash: A4B849A, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: A4B849A, CacheDependencies: Page.
TableRows[0ecebb6e-262d-40a7-9448-b590f4c259c4] added to the cache[KeyHash: A4B849A, CacheDependencies: Page.].
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
Invalidated [EFUnknownsCacheDependency, Page] dependencies.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
[KeyHash: 8C5814DB, CacheDependencies: User.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
TableRows[d66e84d2-f6d0-4ec5-a533-48ef2173e341] added to the cache[KeyHash: 8C5814DB, CacheDependencies: User.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E8A0662F, CacheDependencies: Page.
[KeyHash: E8A0662F, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E8A0662F, CacheDependencies: Page.
TableRows[a5e36d45-e284-4d68-bbeb-a92e53b86c48] added to the cache[KeyHash: E8A0662F, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: EB153BD4, CacheDependencies: Page.
[KeyHash: EB153BD4, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: EB153BD4, CacheDependencies: Page.
TableRows[b757f503-eb85-43ec-9f2c-9df7e52561e5] added to the cache[KeyHash: EB153BD4, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E2FAFD43, CacheDependencies: Page.
[KeyHash: E2FAFD43, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E2FAFD43, CacheDependencies: Page.
TableRows[53dea9c1-838f-4fee-88eb-78fb6fcffd8e] added to the cache[KeyHash: E2FAFD43, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: EB153BD4, CacheDependencies: Page.
Suppressed result with a TableRows[b757f503-eb85-43ec-9f2c-9df7e52561e5] from the cache[KeyHash: EB153BD4, CacheDependencies: Page.].
Using the TableRows[b757f503-eb85-43ec-9f2c-9df7e52561e5] from the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E2FAFD43, CacheDependencies: Page.
Suppressed result with a TableRows[53dea9c1-838f-4fee-88eb-78fb6fcffd8e] from the cache[KeyHash: E2FAFD43, CacheDependencies: Page.].
Using the TableRows[53dea9c1-838f-4fee-88eb-78fb6fcffd8e] from the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
[KeyHash: 8C5814DB, CacheDependencies: User.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
TableRows[9ab61d14-7b5d-447e-bd92-e4f1625687d8] added to the cache[KeyHash: 8C5814DB, CacheDependencies: User.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
[KeyHash: BC1A4D8, CacheDependencies: Settings.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
TableRows[5de34ea8-5a04-4cf2-b59d-3eada8ad4413] added to the cache[KeyHash: BC1A4D8, CacheDependencies: Settings.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
[KeyHash: 8C5814DB, CacheDependencies: User.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
TableRows[532605a1-6710-4441-8077-6d37293304b5] added to the cache[KeyHash: 8C5814DB, CacheDependencies: User.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
[KeyHash: BC1A4D8, CacheDependencies: Settings.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
TableRows[3e99c350-96bc-4bd9-a8a4-a98ac81963a4] added to the cache[KeyHash: BC1A4D8, CacheDependencies: Settings.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
[KeyHash: 8C5814DB, CacheDependencies: User.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
TableRows[bbbe60e1-67e8-41de-8f62-c7846309781c] added to the cache[KeyHash: 8C5814DB, CacheDependencies: User.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
[KeyHash: BC1A4D8, CacheDependencies: Settings.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
TableRows[e25a15c5-e480-493b-ba00-91fe3631489c] added to the cache[KeyHash: BC1A4D8, CacheDependencies: Settings.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
[KeyHash: 8C5814DB, CacheDependencies: User.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
TableRows[c36c7da2-35d2-444c-9ee3-03cb96106b6e] added to the cache[KeyHash: 8C5814DB, CacheDependencies: User.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: 41765E39, CacheDependencies: Page.
[KeyHash: 41765E39, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: 41765E39, CacheDependencies: Page.
TableRows[5e636e9c-e239-4774-b488-85914ccfcfbe] added to the cache[KeyHash: 41765E39, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: EB153BD4, CacheDependencies: Page.
[KeyHash: EB153BD4, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: EB153BD4, CacheDependencies: Page.
TableRows[c8a16eee-6ba6-45cf-a186-f3f506f58d48] added to the cache[KeyHash: EB153BD4, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E7E98500, CacheDependencies: Page.
[KeyHash: E7E98500, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E7E98500, CacheDependencies: Page.
TableRows[cdda302f-2a9c-4aab-9e1c-c4c842bc466f] added to the cache[KeyHash: E7E98500, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: EB153BD4, CacheDependencies: Page.
Suppressed result with a TableRows[c8a16eee-6ba6-45cf-a186-f3f506f58d48] from the cache[KeyHash: EB153BD4, CacheDependencies: Page.].
Using the TableRows[c8a16eee-6ba6-45cf-a186-f3f506f58d48] from the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E7E98500, CacheDependencies: Page.
Suppressed result with a TableRows[cdda302f-2a9c-4aab-9e1c-c4c842bc466f] from the cache[KeyHash: E7E98500, CacheDependencies: Page.].
Using the TableRows[cdda302f-2a9c-4aab-9e1c-c4c842bc466f] from the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: 554A9848, CacheDependencies: Page.
[KeyHash: 554A9848, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: 554A9848, CacheDependencies: Page.
TableRows[e57c3c8e-dc60-4cc9-806b-e629070069ed] added to the cache[KeyHash: 554A9848, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: 554A9848, CacheDependencies: Page.
Suppressed result with a TableRows[e57c3c8e-dc60-4cc9-806b-e629070069ed] from the cache[KeyHash: 554A9848, CacheDependencies: Page.].
Using the TableRows[e57c3c8e-dc60-4cc9-806b-e629070069ed] from the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E7E98500, CacheDependencies: Page.
Suppressed result with a TableRows[cdda302f-2a9c-4aab-9e1c-c4c842bc466f] from the cache[KeyHash: E7E98500, CacheDependencies: Page.].
Using the TableRows[cdda302f-2a9c-4aab-9e1c-c4c842bc466f] from the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: C867BDA8, CacheDependencies: Page.
[KeyHash: C867BDA8, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: C867BDA8, CacheDependencies: Page.
TableRows[1f68b892-fd3e-4ba8-96b7-56aaa0d6b39a] added to the cache[KeyHash: C867BDA8, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E2FAFD43, CacheDependencies: Page.
[KeyHash: E2FAFD43, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E2FAFD43, CacheDependencies: Page.
TableRows[16526b30-c539-49ff-a6c3-6c5aa5a95cf7] added to the cache[KeyHash: E2FAFD43, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: EB153BD4, CacheDependencies: Page.
Suppressed result with a TableRows[c8a16eee-6ba6-45cf-a186-f3f506f58d48] from the cache[KeyHash: EB153BD4, CacheDependencies: Page.].
Using the TableRows[c8a16eee-6ba6-45cf-a186-f3f506f58d48] from the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: E7E98500, CacheDependencies: Page.
Suppressed result with a TableRows[cdda302f-2a9c-4aab-9e1c-c4c842bc466f] from the cache[KeyHash: E7E98500, CacheDependencies: Page.].
Using the TableRows[cdda302f-2a9c-4aab-9e1c-c4c842bc466f] from the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: 554A9848, CacheDependencies: Page.
Suppressed result with a TableRows[e57c3c8e-dc60-4cc9-806b-e629070069ed] from the cache[KeyHash: 554A9848, CacheDependencies: Page.].
Using the TableRows[e57c3c8e-dc60-4cc9-806b-e629070069ed] from the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: 554A9848, CacheDependencies: Page.
Suppressed result with a TableRows[e57c3c8e-dc60-4cc9-806b-e629070069ed] from the cache[KeyHash: 554A9848, CacheDependencies: Page.].
Using the TableRows[e57c3c8e-dc60-4cc9-806b-e629070069ed] from the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: 551915EF, CacheDependencies: Page.
[KeyHash: 551915EF, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: 551915EF, CacheDependencies: Page.
TableRows[c4a609f6-4aab-4f1f-9cb0-6013f1b2508a] added to the cache[KeyHash: 551915EF, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: 554A9848, CacheDependencies: Page.
Suppressed result with a TableRows[e57c3c8e-dc60-4cc9-806b-e629070069ed] from the cache[KeyHash: 554A9848, CacheDependencies: Page.].
Using the TableRows[e57c3c8e-dc60-4cc9-806b-e629070069ed] from the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: 788B1A84, CacheDependencies: Page.
[KeyHash: 788B1A84, CacheDependencies: Page.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: 788B1A84, CacheDependencies: Page.
TableRows[bf23dc38-e572-4190-9d69-393f320a8beb] added to the cache[KeyHash: 788B1A84, CacheDependencies: Page.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Page -> CacheDependencies: Page.
KeyHash: 554A9848, CacheDependencies: Page.
Suppressed result with a TableRows[e57c3c8e-dc60-4cc9-806b-e629070069ed] from the cache[KeyHash: 554A9848, CacheDependencies: Page.].
Using the TableRows[e57c3c8e-dc60-4cc9-806b-e629070069ed] from the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
[KeyHash: 8C5814DB, CacheDependencies: User.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
TableRows[f7096d2c-fc59-47e1-9f2d-62c73d233cc0] added to the cache[KeyHash: 8C5814DB, CacheDependencies: User.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
[KeyHash: BC1A4D8, CacheDependencies: Settings.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
TableRows[ae6f90ce-b09a-4d92-ba2e-97a20b66ebac] added to the cache[KeyHash: BC1A4D8, CacheDependencies: Settings.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
[KeyHash: 8C5814DB, CacheDependencies: User.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
TableRows[2276c2c4-6d2d-4b15-8c57-fefbf892c598] added to the cache[KeyHash: 8C5814DB, CacheDependencies: User.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
[KeyHash: BC1A4D8, CacheDependencies: Settings.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
TableRows[f7be08ba-80cb-423b-9b5c-72cce35231d4] added to the cache[KeyHash: BC1A4D8, CacheDependencies: Settings.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
[KeyHash: 8C5814DB, CacheDependencies: User.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: User -> CacheDependencies: User.
KeyHash: 8C5814DB, CacheDependencies: User.
TableRows[f0fb7bc5-f336-412d-88a8-b7b26fc87a80] added to the cache[KeyHash: 8C5814DB, CacheDependencies: User.].
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
[KeyHash: BC1A4D8, CacheDependencies: Settings.] was not present in the cache.
Using EFCachePolicy: EFCachePolicy[(line 0)] --> Absolute|00:30:00|||False
ContextTableNames: Page, Role, Settings, User, PossibleQueryTableNames: Settings -> CacheDependencies: Settings.
KeyHash: BC1A4D8, CacheDependencies: Settings.
TableRows[95ff3ecd-e0b8-4f74-b34c-e0ff1d83f6bd] added to the cache[KeyHash: BC1A4D8, CacheDependencies: Settings.].

Possibly change to Debug

There's a TON of logging that the cache code is dumping into our production logs. Can we get this to work like the previous version? No logging unless it's in debug mode?

This is flooding production level deployments that keep Info on.

EasyCaching Hybrid

Hi there,

Finally switching from CacheManager to EasyCache. Trying to set up the hybrid solution to let me use InMemory and then just use Redis as the backplane. The setup from https://easycaching.readthedocs.io/en/latest/HybridCachingProvider/#how-to-use is very straight forward, however, the last line of code UseEasyCacheCoreProvider only works if I set it to "memoryProvider" or "redisProvider". If I set it to "hybridProvider" it throws an error: "can not find a match caching provider!"

Is there an easy way to add support for this?

const string memoryProvider = "memory";
            const string redisProvider = "redis";
            const string hybridProvider = "hybrid";
            const string redisBusProvider = "bus";
                services.AddEasyCaching(option =>
                {
                      option.UseInMemory(config =>
                        {
                            config.DBConfig = new InMemoryCachingOptions
                                              {
                                                  ExpirationScanFrequency = 60,
                                                  SizeLimit = 10000,
                                                  EnableReadDeepClone = false,
                                                  EnableWriteDeepClone = false
                                              };
                            config.MaxRdSecond = 120;
                            config.EnableLogging = true;
                            config.LockMs = 5000;
                            config.SleepMs = 300;
                        },
                        memoryProvider).UseRedis(config =>
                        {
                            config.DBConfig.Endpoints.Add(
                                new EasyCaching.Core.Configurations.ServerEndPoint(configuration["Redis:Server"], configuration["Redis.Port"].ToInt32()));
                            config.DBConfig.Database = 1;
                            config.DBConfig.Password = configuration["Redis:Password"];
                            config.DBConfig.AllowAdmin = true;
                            config.DBConfig.IsSsl = true;
                            config.EnableLogging = true;
                        },
                        redisProvider).UseHybrid(config =>
                        {
                            config.TopicName = "topic";
                            config.EnableLogging = true;
                            config.LocalCacheProviderName = memoryProvider;
                            config.DistributedCacheProviderName = redisProvider;
                        },
                        hybridProvider).WithRedisBus(busConf =>
                        {
                            busConf.Endpoints.Add(
                                new EasyCaching.Core.Configurations.ServerEndPoint(configuration["Redis:Server"], configuration["Redis.Port"].ToInt32()));
                            busConf.Database = 2;
                            busConf.Password = configuration["Redis:Password"];
                            busConf.AllowAdmin = true;
                            busConf.IsSsl = true;
                        });
                });

                services.AddEFSecondLevelCache(options => options.UseEasyCachingCoreProvider(memoryProvider)); //.DisableLogging(true));

Adding prefix before the instance name for Redis

Summary of the issue

I am working on multiple projects with multiple databases and I would like to know if it's possible to add a prefix specifying the project's name to make it easier for me to delete entries from Cache belonging to a specific project.

Microsoft.EntityFrameworkCore version: 3.1

How to use in a separate project

Hi,
My solution consists of two projects: one for the web stuff (ASP.NET Core MVC 3.1) and one for the data stuff (entities, DbContext, repositories etc.. using EF Core 3.1).
Now, I'd like to add your library in the data project only: can you please show my how to do that?

Thank you.
Best regards.

Set Global Settings For Cacheable Extension (Not CacheAllQueries)

Issue

How to set global settings for .Cacheable() instead of writing .Cacheable(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(5)) for every query we want to cache using default settings? Something like options available for CacheAllQueries method.

And what is the default expiration options for .Cacheable() anyway?
`

Support .NET Core 3.1

Summary of the issue

It's seems .NET Core 3.1 no longer been supported, right ?
Unable to update to v.1.9.1 in NUGET !

Environment

.NET Core SDK version: 
Microsoft.EntityFrameworkCore version: 3.1.9
EFCoreSecondLevelCacheInterceptor version: 1.9.0
Exception message: 
Severity	Code	Description	Project	File	Line	Suppression State
Error	NU1605	Detected package downgrade: Microsoft.EntityFrameworkCore from 5.0.0-rc.2.20475.6 to 3.1.9. Reference the package directly from the project to select a different version. 
 R8.Lib.EntityFrameworkCore -> EFCoreSecondLevelCacheInterceptor 1.9.1 -> Microsoft.EntityFrameworkCore (>= 5.0.0-rc.2.20475.6) 
 R8.Lib.EntityFrameworkCore -> Microsoft.EntityFrameworkCore (>= 3.1.9)	R8.Lib.EntityFrameworkCore	E:\Work\Develope\Asp\Ecohos\R8.Lib.EntityFrameworkCore\R8.Lib.EntityFrameworkCore.csproj	1	

Full Stack trace:

When using CacheManager for redis cache, TimeSpan cache is not converted properly

Summary of the issue

I recently migrating our service from EFSecondLevelCache.Core to EFCoreSecondLevelCacheInterceptor.
It was caching the TimeSpan properly before, but after change to use EFCoreSecondLevelCacheInterceptor, without changing the CacheManager implementation and JsonSerializer, when query from redis cache, it throws

System.InvalidCastException: Unable to cast object of type 'System.String' to type 'System.TimeSpan'.

I checked the redis record and saw that the JSON is something like

{
	"TableRows": {
		"Rows": [{
				"Values": [1608627, 1608627, "18:00:00", "08:30:00", 1608627, "18:00:00", "08:30:00", 1608627, "00:00:00", "00:00:00", 1608627, "00:00:00", "00:00:00", 1608627, "18:00:00", "08:30:00", 1608627, "18:00:00", "08:30:00", 1608627, "18:00:00", "08:30:00", 1608627, 1608526, "2020-07-31T00:00:00", "Hari Raya Haji", "00:00:00", "00:00:00"],
				"Depth": 0,
				"FieldCount": 28
			}, {
				"Values": [1608627, 1608627, "18:00:00", "08:30:00", 1608627, "18:00:00", "08:30:00", 1608627, "00:00:00", "00:00:00", 1608627, "00:00:00", "00:00:00", 1608627, "18:00:00", "08:30:00", 1608627, "18:00:00", "08:30:00", 1608627, "18:00:00", "08:30:00", 1608627, 1608526, "2020-08-09T00:00:00", "National Day", "00:00:00", "00:00:00"],
				"Depth": 0,
				"FieldCount": 28
			}, {
				"Values": [1608627, 1608627, "18:00:00", "08:30:00", 1608627, "18:00:00", "08:30:00", 1608627, "00:00:00", "00:00:00", 1608627, "00:00:00", "00:00:00", 1608627, "18:00:00", "08:30:00", 1608627, "18:00:00", "08:30:00", 1608627, "18:00:00", "08:30:00", 1608627, 1608526, "2020-08-10T00:00:00", "The day after National Day", "00:00:00", "00:00:00"],
				"Depth": 0,
				"FieldCount": 28
			}, {
				"Values": [1608627, 1608627, "18:00:00", "08:30:00", 1608627, "18:00:00", "08:30:00", 1608627, "00:00:00", "00:00:00", 1608627, "00:00:00", "00:00:00", 1608627, "18:00:00", "08:30:00", 1608627, "18:00:00", "08:30:00", 1608627, "18:00:00", "08:30:00", 1608627, 1608526, "2020-11-14T00:00:00", "Deepavali", "00:00:00", "00:00:00"],
				"Depth": 0,
				"FieldCount": 28
			}, {
				"Values": [1608627, 1608627, "18:00:00", "08:30:00", 1608627, "18:00:00", "08:30:00", 1608627, "00:00:00", "00:00:00", 1608627, "00:00:00", "00:00:00", 1608627, "18:00:00", "08:30:00", 1608627, "18:00:00", "08:30:00", 1608627, "18:00:00", "08:30:00", 1608627, 1608526, "2020-12-25T00:00:00", "Christmas Day", "00:00:00", "00:00:00"],
				"Depth": 0,
				"FieldCount": 28
			}
		],
		"ColumnsInfo": {
			"0": {
				"Ordinal": 0,
				"Name": "CentreId",
				"DbTypeName": "int",
				"TypeName": "System.Int32"
			},
			"1": {
				"Ordinal": 1,
				"Name": "CentreId0",
				"DbTypeName": "int",
				"TypeName": "System.Int32"
			},
			"2": {
				"Ordinal": 2,
				"Name": "FridayCloseTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			},
			"3": {
				"Ordinal": 3,
				"Name": "FridayOpenTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			},
			"4": {
				"Ordinal": 4,
				"Name": "CentreId1",
				"DbTypeName": "int",
				"TypeName": "System.Int32"
			},
			"5": {
				"Ordinal": 5,
				"Name": "MondayCloseTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			},
			"6": {
				"Ordinal": 6,
				"Name": "MondayOpenTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			},
			"7": {
				"Ordinal": 7,
				"Name": "CentreId2",
				"DbTypeName": "int",
				"TypeName": "System.Int32"
			},
			"8": {
				"Ordinal": 8,
				"Name": "SaturdayCloseTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			},
			"9": {
				"Ordinal": 9,
				"Name": "SaturdayOpenTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			},
			"10": {
				"Ordinal": 10,
				"Name": "CentreId3",
				"DbTypeName": "int",
				"TypeName": "System.Int32"
			},
			"11": {
				"Ordinal": 11,
				"Name": "SundayCloseTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			},
			"12": {
				"Ordinal": 12,
				"Name": "SundayOpenTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			},
			"13": {
				"Ordinal": 13,
				"Name": "CentreId4",
				"DbTypeName": "int",
				"TypeName": "System.Int32"
			},
			"14": {
				"Ordinal": 14,
				"Name": "ThursdayCloseTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			},
			"15": {
				"Ordinal": 15,
				"Name": "ThursdayOpenTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			},
			"16": {
				"Ordinal": 16,
				"Name": "CentreId5",
				"DbTypeName": "int",
				"TypeName": "System.Int32"
			},
			"17": {
				"Ordinal": 17,
				"Name": "TuesdayCloseTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			},
			"18": {
				"Ordinal": 18,
				"Name": "TuesdayOpenTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			},
			"19": {
				"Ordinal": 19,
				"Name": "CentreId6",
				"DbTypeName": "int",
				"TypeName": "System.Int32"
			},
			"20": {
				"Ordinal": 20,
				"Name": "WednesdayCloseTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			},
			"21": {
				"Ordinal": 21,
				"Name": "WednesdayOpenTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			},
			"22": {
				"Ordinal": 22,
				"Name": "CentreId",
				"DbTypeName": "int",
				"TypeName": "System.Int32"
			},
			"23": {
				"Ordinal": 23,
				"Name": "BuildingId",
				"DbTypeName": "int",
				"TypeName": "System.Int32"
			},
			"24": {
				"Ordinal": 24,
				"Name": "Date",
				"DbTypeName": "date",
				"TypeName": "System.DateTime"
			},
			"25": {
				"Ordinal": 25,
				"Name": "HolidayName",
				"DbTypeName": "nvarchar",
				"TypeName": "System.String"
			},
			"26": {
				"Ordinal": 26,
				"Name": "CloseTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			},
			"27": {
				"Ordinal": 27,
				"Name": "OpenTime",
				"DbTypeName": "time",
				"TypeName": "System.TimeSpan"
			}
		},
		"FieldCount": 28,
		"TableName": "db55708c-7d88-4feb-86fe-aacab1421ae2",
		"HasRows": true,
		"RecordsAffected": -1,
		"VisibleFieldCount": 28,
		"RowsCount": 5
	},
	"NonQuery": 0,
	"IsNull": false
}

Environment

.NET Core SDK version: 3.1.302
Microsoft.EntityFrameworkCore version: 3.1.6
EFCoreSecondLevelCacheInterceptor version: 1.8.2

Example code/Steps to reproduce:

In startup

       serviceCollection
                .AddEFSecondLevelCache(opts => opts.UseCacheManagerCoreProvider().DisableLogging(false))
                .AddSingleton(typeof(ICacheManager<>), typeof(BaseCacheManager<>))
                .AddSingleton(typeof(ICacheManagerConfiguration),
                    new ConfigurationBuilder()
                        .WithMaxRetries(100)
                        .WithJsonSerializer(options.JsonSerializerSettings, options.JsonSerializerSettings)
                        .WithRedisConfiguration(RedisConfigurationKey, redisConnectionString)
                        .WithRedisCacheHandle(RedisConfigurationKey)
                        .WithExpiration(options.ExpirationMode, options.Timeout)
                        .Build());
public JsonSerializerSettings JsonSerializerSettings { get; set; } = new JsonSerializerSettings
        {
            NullValueHandling = NullValueHandling.Ignore,
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            TypeNameHandling = TypeNameHandling.Auto,
            ContractResolver = new ContractResolver()
        };

The query

_db.Set<CentreSchedule>
      .Cacheable()
      .FirstOrDefaultAsync(x => x.CentreId == someId, token);

Output:

Exception message: `System.InvalidCastException: Unable to cast object of type 'System.String' to type 'System.TimeSpan'.`
Full Stack trace:
   at System.Data.Common.DbDataReader.GetFieldValue[T](Int32 ordinal)
   at lambda_method(Closure , QueryContext , DbDataReader , ResultContext , Int32[] , ResultCoordinator )
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)

Interceptor is unable to handle DateTimeOffset, even using recommended SpecialTypesConverter

The original issue I logged was related to caching in Redis (#18), but I now have a better handle on the problem, and it's not really related to Redis so I figured I'd log a new issue.

I just had some time to come back to this and do some more digging, and it turns out that the SpecialTypesConverter prevents the app from crashing, but leaves all nullable types null and non-nullable types as their default value.

The following console app demonstrates this:

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace SerializeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var cacheJsonSerializerSettings = new JsonSerializerSettings
            {
                // Types like DateTimeOffset don't work properly with EFCoreSecondLevelCacheInterceptor without storing additional information about the date type
                Converters = { new SpecialTypesConverter() },
                TypeNameHandling = TypeNameHandling.Auto,
                NullValueHandling = NullValueHandling.Ignore,
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,

            };

            var dto = new MyDto
            {
                MyDate = DateTime.Now,
                MyNullableDate = DateTime.Now,
                MyOffset = DateTimeOffset.Now,
                MyNullableOffset = DateTimeOffset.Now
            };

            var json = JsonConvert.SerializeObject(dto, cacheJsonSerializerSettings);
            var dto2 = JsonConvert.DeserializeObject<MyDto>(json, cacheJsonSerializerSettings);
        }
    }

    class MyDto
    {
        public DateTime MyDate { get; set; }
        public DateTime? MyNullableDate { get; set; }
        public DateTimeOffset MyOffset { get; set; }
        public DateTimeOffset? MyNullableOffset { get; set; }
    }

    public class SpecialTypesConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(TimeSpan) || objectType == typeof(TimeSpan?)
                                                  || objectType == typeof(DateTime) || objectType == typeof(DateTime?)
                                                  || objectType == typeof(DateTimeOffset) || objectType == typeof(DateTimeOffset?);
        }

        public override bool CanRead => true;

        public override bool CanWrite => true;

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
            JsonSerializer serializer)
        {

            return reader.Value;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            writer.WriteStartObject();
            writer.WritePropertyName("$type");
            writer.WriteValue(value.GetType().FullName);
            writer.WritePropertyName("$value");
            writer.WriteValue(value);
            writer.WriteEndObject();
        }
    }
}

When trying to implement ReadJson, I fell down a rabbit hole of date parsing.

At the end of the day, I'm trying to determine why this is necessary though. JsonConvert handles serialization of all of these types with no issue. You can see this by taking the above code and simply commenting out the SpecialTypesConverter. Everything serializes and deserializes fine.

EF Core also handles these system types seamlessly.

Finally, the previous version of your package didn't have an issue with any of these types.

I took a look at your implementation and I believe this can be handled entirely within your library, though I'll admit I'm not sure of the best place to handle it.

Using DateTime / DateTimeOffset as an example, the crux of the issue is that after a result is pulled from the cache, despite having the actual type information in ColumnsInfo, the object[] in TableRow.Values has defaulted to DateTime as opposed to the type stored in ColumnsInfo (which in this case is System.DateTimeOffset).

If you disagree that the library should handle this, do you have any suggestions on how it could be implemented in the SpecialTypesConverter without essentially re-implementing Json.NET's handling of it?

InvalidCastException Int64 -> Int32 with SQLite

Summary of the issue

I am using SQLite with Entity Framework Core and getting an error if i want to cache data. The error only occous if i want to cache it globally or manually one by one.

Environment

.NET Core SDK version: 3.1.2
Microsoft.EntityFrameworkCore version: 3.1.2
EFCoreSecondLevelCacheInterceptor version: 1.12

Example code/Steps to reproduce:

as in your example.

Output:

Exception message:
An unhandled exception occurred while processing the request.
InvalidCastException: Unable to cast object of type 'System.Int64' to type 'System.Int32'.
EFCoreSecondLevelCacheInterceptor.EFTableRowsDataReader.GetInt32(int ordinal)

Full Stack trace:
InvalidCastException: Unable to cast object of type 'System.Int64' to type 'System.Int32'.
EFCoreSecondLevelCacheInterceptor.EFTableRowsDataReader.GetInt32(int ordinal)
lambda_method(Closure , QueryContext , DbDataReader , ResultContext , int[] , ResultCoordinator )
Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable<T>+Enumerator.MoveNext()
System.Linq.Enumerable+SelectEnumerableIterator<TSource, TResult>.ToList()
System.Linq.Enumerable.ToList<TSource>(IEnumerable<TSource> source)
spots.Services.SpotsService.UpdateSpotViewModel(SpotsViewModel spotsViewModel, bool mySpots, int groupTab) in SpotsService.cs
+
            spotsViewModel.Spots = spots

Cannot have 2 or more OwnsOnes of the same type on a model.

Summary of the issue

Cannot have 2 or more OwnsOnes of the same type on a model.

A potential fix for our issue could be inside of EFSqlCommandsProcessor.GetAllTableNames to change the line tableNames.Add(entityType.ClrType, entityType.GetTableName()); to use a TryAdd() instead of Add()

Environment

.NET Core SDK version: 3.1
Microsoft.EntityFrameworkCore version: 3.1.6
EFCoreSecondLevelCacheInterceptor version: 1.8.0

Example code/Steps to reproduce:

protected override void OnModelCreating(ModelBuilder builder)
{
    builder.Entity<EngineVersion>().OwnsOne(p => p.Commercial);
    builder.Entity<EngineVersion>().OwnsOne(p => p.Retail);
}
public class EngineVersion
{
    public int Id { get; set; }

    public EngineProductVersion Commercial { get; set; }
    public EngineProductVersion Retail { get; set; }
}

public class EngineProductVersion
{
    public int Major { get; set; }
    public int Minor { get; set; }
    public int Revision { get; set; }
    public int Patch { get; set; }

    public override string ToString()
        =>  $"{Major}.{Minor}.{Revision}.{Patch}";
}

Output:

System.ArgumentException: An item with the same key has already been added. Key: Application.Core.Models.EngineProductVersion
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at EFCoreSecondLevelCacheInterceptor.EFSqlCommandsProcessor.<>c__DisplayClass3_0.<GetAllTableNames>b__1()
   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
   at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
   at System.Lazy`1.CreateValue()
   at System.Lazy`1.get_Value()
   at EFCoreSecondLevelCacheInterceptor.EFSqlCommandsProcessor.GetAllTableNames(DbContext context)
   at EFCoreSecondLevelCacheInterceptor.DbCommandInterceptorProcessor.ProcessExecutingCommands[T](DbCommand command, DbContext context, T result)
   at EFCoreSecondLevelCacheInterceptor.SecondLevelCacheInterceptor.NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult`1 result)
   at Microsoft.EntityFrameworkCore.Diagnostics.RelationalLoggerExtensions.CommandNonQueryExecuting(IDiagnosticsLogger`1 diagnostics, IRelationalConnection connection, DbCommand command, DbContext context, Guid commandId, Guid connectionId, DateTimeOffset startTime)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteNonQuery(RelationalCommandParameterObject parameterObject)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerDatabaseCreator.<>c__DisplayClass18_0.<Exists>b__0(DateTime giveUp)
   at Microsoft.EntityFrameworkCore.ExecutionStrategyExtensions.<>c__DisplayClass12_0`2.<Execute>b__0(DbContext c, TState s)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
   at Microsoft.EntityFrameworkCore.ExecutionStrategyExtensions.Execute[TState,TResult](IExecutionStrategy strategy, TState state, Func`2 operation, Func`2 verifySucceeded)
   at Microsoft.EntityFrameworkCore.ExecutionStrategyExtensions.Execute[TState,TResult](IExecutionStrategy strategy, TState state, Func`2 operation)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerDatabaseCreator.Exists(Boolean retryOnNotExists)   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerDatabaseCreator.Exists()
   at Microsoft.EntityFrameworkCore.Storage.RelationalDatabaseCreator.CanConnect()
   at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.CanConnect()
   at Application.API.Program.WaitForDatabaseToExistAndMigrate(IServiceProvider services) in C:\Projects\Application\Master\src\Application.API\Program.cs:line 106
   at Application.API.Program.Main(String[] args) in C:\Projects\Application\Master\src\Application.API\Program.cs:line 67

cache suitable for joins?

Hi,
I have some big data that rarely change. for example 'Divisions of provinces and cities'.
but this tables always used in the join with some other tables. now how I can used my table cache in Join, include() ?

thanks

Using Cacheable() in explicit loading

Summary of the issue

Hi,
Keep up the good work.

Can I use Cacheable in an explicit loading scenario?

be like:

await dbcontext.Entry(root).Reference(x => x.SubClass).Query().Cacheable().LoadAsync()

When I use it like that, I get an instance of the object filled with nulls.

Environment

.NET Core SDK version: 3.1.1
Microsoft.EntityFrameworkCore version: 3.1.1
EFCoreSecondLevelCacheInterceptor version: 1.0.0

The nuget package for .NETStandard, Version 2.1 has dependency on Microsoft.EntityFrameworkCore on 5.0.0-rc.2.20475.6 instead of 3.1.6.

Summary of the issue

The nuget package for .NETStandard, Version 2.1 has dependency on Microsoft.EntityFrameworkCore on 5.0.0-rc.2.20475.6 instead of 3.1.6.

Environment

.NET Core SDK version: .NETStandard v2.1
Microsoft.EntityFrameworkCore version: 3.1.6
EFCoreSecondLevelCacheInterceptor version: 2.0.0

Example code/Steps to reproduce:

Try to install EFCoreSecondLevelCacheInterceptor in a .NETStandard v2.1 library

Exception message:
Full Stack trace:

Detected package downgrade: Microsoft.EntityFrameworkCore from 5.0.0-rc.2.20475.6 to 3.1.6. Reference the package directly from the project to select a different version.
EFCoreSecondLevelCacheInterceptor 2.0.0 -> Microsoft.EntityFrameworkCore (>= 5.0.0-rc.2.20475.6)

Question - EFCachePolicy

Hello,

I am trying to update the EFCachePolicy in particular the CacheDependencies as my table names are all appended with
dbo.
command text:

`
SELECT TOP(1) [u].[Id], [u].[AccessFailedCount], [u].[ConcurrencyStamp], [u].[Email], [u].[EmailConfirmed], [u].[LockoutEnabled], [u].[LockoutEnd], [u].[NormalizedEmail], [u].[NormalizedUserName], [u].[ObjectRowState], [u].[PasswordHash], [u].[PhoneNumber], [u].[PhoneNumberConfirmed], [u].[SecurityStamp], [u].[TwoFactorEnabled], [u].[UserName]
FROM [dbo].[Users] AS [u]
WHERE [u].[NormalizedUserName] = @__normalizedUserName_0

`

Within the EFCacheDependenciesProcessor class and the method GetCacheDependencies, the argument EFCachePolicy does not match the policy I configure within the Startup

EFCachePolicy.Configure(options => { options.CacheDependencies("dbo.Users", "dbo.Table2"); });

When debugging the Table Names inferred from the above command text results in "dbo" and this results in the EFUnknownsCacheDependency within the cache due to fact that it finds tables with 'dbo' instead of the full table name.

How do I properly configure this?

Thanks.

System.InvalidCastException on MySQL Pomelo

Summary of the issue

Using EF Core Pomelo Mysql and Redis. Configured with Cachemanagercore as recommended in the documentstion.
Error:

System.InvalidCastException: Unable to cast object of type 'System.Int32' to type 'System.Boolean'.
   at EFCoreSecondLevelCacheInterceptor.EFTableRowsDataReader.GetBoolean(Int32 ordinal)
   at lambda_method(Closure , QueryContext , DbDataReader , ResultContext , Int32[] , ResultCoordinator )
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()

Environment

.NET Core SDK version: 3.1
Microsoft.EntityFrameworkCore version: 3.1
EFCoreSecondLevelCacheInterceptor version:  1.5.4

Example code/Steps to reproduce:

Can't post the code sample as it's confidential. But the models in the repository used have properties with datatypes Guid, string, boolean, Datetime, and int.

Output:

Exception message:
Full Stack trace:
System.InvalidCastException: Unable to cast object of type 'System.Int32' to type 'System.Boolean'.
   at EFCoreSecondLevelCacheInterceptor.EFTableRowsDataReader.GetBoolean(Int32 ordinal)
   at lambda_method(Closure , QueryContext , DbDataReader , ResultContext , Int32[] , ResultCoordinator )
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at MyOrg.Application.Service.UserQuizRooms.UserQuizRoomsService.GetHistoryAsync(Guid userId) in /home/adis/projects/dotnet/MyApp/src/Services/Quiz/MyApp.Application/Service/UserQuizRooms/UserQuizRoomsService.cs:line 329
   at MyOrg.Controllers.UserQuizRoomsController.GetHistoryAsync() in /home/adis/projects/dotnet/MyApp/src/Services/Quiz/MyApp.Presentation/Controllers/UserQuizRoomsController.cs:line 171
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Support for spatial type

I thought EF Core does the conversion for Spatial Type itself before EfCache gets its value...

InvalidCastException: Unable to cast object of type 'Microsoft.SqlServer.Types.SqlGeography' to type 'Microsoft.Data.SqlClient.Server.IBinarySerialize'.

Relevant Stacktrace:

Microsoft.Data.SqlClient.Server.BinarySerializeSerializer.Deserialize(Stream s)
Microsoft.Data.SqlClient.Server.SerializationHelperSql9.Deserialize(Stream s, Type resultType)
Microsoft.Data.SqlClient.SqlConnection.GetUdtValue(object value, SqlMetaDataPriv metaData, bool returnDBNull)
Microsoft.Data.SqlClient.SqlDataReader.GetValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData metaData)
Microsoft.Data.SqlClient.SqlDataReader.GetValues(object[] values)
EFCoreSecondLevelCacheInterceptor.EFDataReaderLoader.Read()
EFCoreSecondLevelCacheInterceptor.EFDataReaderLoader.LoadAndClose()

Unable to use Azure Cache for Redis

Summary of the issue

I just switched from EFCoreSecondLevelCache (the old package) to this new package, and I'm unable to get Redis caching to work using Azure Cache for Redis. I have a Standard Tier Redis cache configured in Azure like so:

image

The Redis cache config looks like this:

                cacheConfigurationBuilder
                    .WithJsonSerializer(cacheJsonSerializerSettings, cacheJsonSerializerSettings)
                    .WithRedisConfiguration(RedisCacheConfigKey, config =>
                    {
                        config.WithAllowAdmin()
                            .WithPassword(redisConfig.Password)
                            .WithDatabase(0)
                            .WithSsl(redisConfig.Endpoint)
                            .WithEndpoint(redisConfig.Endpoint, redisConfig.Port)
                        .EnableKeyspaceEvents();
                    })
                    .WithRedisCacheHandle(RedisCacheConfigKey)
                    .WithExpiration(ExpirationMode.Absolute, TimeSpan.FromMinutes(10));

With all of the above, any query run against the DbContext throws this exception:

'ERR unknown command CONFIG, with args beginning with: GET, notify-keyspace-events, '

Stack Trace:

at StackExchange.Redis.ConnectionMultiplexer.ExecuteSyncImpl[T](Message message, ResultProcessor1 processor, ServerEndPoint server) in C:\projects\stackexchange-redis\src\StackExchange.Redis\ConnectionMultiplexer.cs:line 2231 at StackExchange.Redis.RedisBase.ExecuteSync[T](Message message, ResultProcessor1 processor, ServerEndPoint server) in C:\projects\stackexchange-redis\src\StackExchange.Redis\RedisBase.cs:line 54
at StackExchange.Redis.RedisServer.ExecuteSync[T](Message message, ResultProcessor1 processor, ServerEndPoint server) in C:\projects\stackexchange-redis\src\StackExchange.Redis\RedisServer.cs:line 585 at StackExchange.Redis.RedisServer.ConfigGet(RedisValue pattern, CommandFlags flags) in C:\projects\stackexchange-redis\src\StackExchange.Redis\RedisServer.cs:line 152 at CacheManager.Redis.RedisConnectionManager.GetConfiguration(String key) at CacheManager.Redis.RedisCacheHandle1..ctor(ICacheManagerConfiguration managerConfiguration, CacheHandleConfiguration configuration, ILoggerFactory loggerFactory, ICacheSerializer serializer)

Updated Config to use Redis for Backplane only

Hi there, this is a great update! However, it broke our configuration that we had for using an in-memory but Redis as a backplane to coordinate invalidation across servers.

Can you provide an updates configuration sample for doing this? Maybe even a template project?

Cannot make it work

Summary of the issue

Hi, i've followed the steps you describe in the manual.
I've tried both EasyCaching.Core and CacheManager.Core.
Both times I can see the queries using Sql Server Profiler.
There's no exception, nothing going wrong, the queries are marked in the profiler with a tag ("--EFCachePolicy etc...",
but the cache never seems to get hit. Queries always go through.

I probably miss something.
Could you please help ?

Environment

.net core api using sql server as database

.NET Core SDK version: 3.1
Microsoft.EntityFrameworkCore version: 3.1.3
EFCoreSecondLevelCacheInterceptor version: 1.7

Example code/Steps to reproduce:

/*
** Startup()
*/
ConfigureCaching(services);
// ...
        private void ConfigureCaching(IServiceCollection services)
        {
            services.AddEFSecondLevelCache(options =>
                options.UseCacheManagerCoreProvider().DisableLogging(true)
            );

            // Add an in-memory cache service provider
            services.AddSingleton(typeof(ICacheManager<>), typeof(BaseCacheManager<>));
            services.AddSingleton(typeof(ICacheManagerConfiguration),
                new CacheManager.Core.ConfigurationBuilder()
                        .WithJsonSerializer()
                        .WithMicrosoftMemoryCacheHandle(instanceName: "MemoryCache1")
                        .Build());
        }

/*
** Usage
*/

        private Task<Dictionary<Guid, Specialties>> GetCachedSpecialties() 
            => _specialtiesRepository
                    .GetAll()
                    .Cacheable(CacheExpirationMode.Absolute, TimeSpan.FromHours(24))
                    .ToDictionaryAsync(s=> s.IdSpecialty);

        private Task<Dictionary<string,ShortDoctorDto>> GetCachedDoctors()
            => _usersService
                    .GetShortDoctors()
                    .Cacheable(CacheExpirationMode.Absolute, TimeSpan.FromHours(1))
                    .ToDictionaryAsync(s=> s.IdUser.ToString().ToLower());

Output:

image

Exception message:
Full Stack trace:

Clarification requested on invalidation

From the readme:

Invalidation

This cache is updated when an entity is changed (insert, update, or delete) via a DbContext that uses this library. If the database is updated through some other means, such as a stored procedure or trigger, the cache becomes stale.

I expect that the cache is updated when an entity is changed in the relevant DbSet. Is that correct? Or are all caches invalidated when any entity has been touched?

Consider maintain EFCoreSecondLevelCacheInterceptor.Redis separately?

Summary of the issue

Now the package depends on Redis related packages such as Microsoft.Extensions.Caching.StackExchangeRedis, how about keep different provider implementations separately?

Environment

.NET Core SDK version: 3.1.100
Microsoft.EntityFrameworkCore version: 3.1.1
EFCoreSecondLevelCacheInterceptor version: 1.0.0

Example code/Steps to reproduce:

dotnet add package EFCoreSecondLevelCacheInterceptor

Output:

Restoring Microsoft.Extensions.Caching.StackExchangeRedis

Find/FindAsync not supported - Cacheable().FindAsync or Cacheable().Find does not compile

Summary of the issue

Cacheable().FindAsync gives a compilation error as below:

CS1061 'IQueryable' does not contain a definition for 'FindAsync' and no accessible extension method 'FindAsync' accepting a first argument of type 'IQueryable' could be found (are you missing a using directive or an assembly reference?) AIMunshi API Data ....\User.cs

SingeOrDefaultAsync, FirstOrDefaultAsync, etc... works

Environment

.NET Core SDK version:  3.1.3
Microsoft.EntityFrameworkCore version: 3.1.3
EFCoreSecondLevelCacheInterceptor version: 1.31

Example code/Steps to reproduce:

await context.Users.FindAsync(userId); // this is ok

await context.Users.Cacheable().FindAsync(userId); // does not compile

Output:

Exception message:
CS1061	'IQueryable<User>' does not contain a definition for 'FindAsync' and no accessible extension method 'FindAsync' accepting a first argument of type 'IQueryable<User>' could be found (are you missing a using directive or an assembly reference?)	AIMunshi API Data	....\User.cs	

Full Stack trace:

Breaking change after upgrading to EasyCaching 0.8.10

Summary of the issue

Upon upgrading EasyCaching to 0.8.10, a new exception is thrown.

Environment

.NET Core SDK version: 3.1.302
Microsoft.EntityFrameworkCore version: 3.1.6
EFCoreSecondLevelCacheInterceptor version: 1.8.1

Example code/Steps to reproduce:

pretty much the basic setup as described by the tutorial, with redis as cache store.

Output:

System.Exception: Method not found: 'EasyCaching.Core.CacheValue`1<!!0> EasyCaching.Core.IEasyCachingProvider.Get(System.String)'.
   at EFCachedData EFCoreSecondLevelCacheInterceptor.EFEasyCachingCoreProvider.GetValue(EFCacheKey cacheKey, EFCachePolicy cachePolicy)+() => { }
   at T EFCoreSecondLevelCacheInterceptor.ReaderWriterLockProvider.TryReadLocked<T>(Func<T> action, int timeout)
   at EFCachedData EFCoreSecondLevelCacheInterceptor.EFEasyCachingCoreProvider.GetValue(EFCacheKey cacheKey, EFCachePolicy cachePolicy)
   at T EFCoreSecondLevelCacheInterceptor.DbCommandInterceptorProcessor.ProcessExecutingCommands<T>(DbCommand command, DbContext context, T result)
   at Task<InterceptionResult<DbDataReader>> EFCoreSecondLevelCacheInterceptor.SecondLevelCacheInterceptor.ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result, CancellationToken cancellationToken)
   at Task<InterceptionResult<DbDataReader>> Microsoft.EntityFrameworkCore.Diagnostics.RelationalLoggerExtensions.CommandReaderExecutingAsync(IDiagnosticsLogger<Command> diagnostics, IRelationalConnection connection, DbCommand command, DbContext context, Guid commandId, Guid connectionId, DateTimeOffset startTime, CancellationToken cancellationToken)
   at async Task<RelationalDataReader> Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at async Task<RelationalDataReader> Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at async Task<RelationalDataReader> Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at async Task<bool> Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable<T>+AsyncEnumerator.InitializeReaderAsync(DbContext _, bool result, CancellationToken cancellationToken)
   at async Task<TResult> Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync<TState, TResult>(Func<DbContext, TState, CancellationToken, Task<TResult>> operation, Func<DbContext, TState, CancellationToken, Task<ExecutionResult<TResult>>> verifySucceeded, TState state, CancellationToken cancellationToken)
   at async Task<TResult> Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync<TState, TResult>(Func<DbContext, TState, CancellationToken, Task<TResult>> operation, Func<DbContext, TState, CancellationToken, Task<ExecutionResult<TResult>>> verifySucceeded, TState state, CancellationToken cancellationToken)
   at async ValueTask<bool> Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable<T>+AsyncEnumerator.MoveNextAsync()
   at TResult System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable<TResult>+ConfiguredValueTaskAwaiter.GetResult()
   at async Task<List<TSource>> Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync<TSource>(IQueryable<TSource> source, CancellationToken cancellationToken)
   at async Task<List<TSource>> Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync<TSource>(IQueryable<TSource> source, CancellationToken cancellationToken)

Unable to cast object of type 'MySql.Data.Types.MySqlDateTime' to type 'System.DateTime'

Summary of the issue

I use Pomelo.EntityFrameworkCore.MySql .The following error was reported when I was running
my dataconnectstring: persistsecurityinfo=True;server=xxxxxx;port=3300;user id=xxxxx;password=xxxxxx;database=wuziwebsite_dev;AllowZeroDateTime=true;ConvertZeroDateTime=true

Environment

.NET Core SDK version: 3.1
Microsoft.EntityFrameworkCore version: 3.1
EFCoreSecondLevelCacheInterceptor version: 1.3.1

Example code/Steps to reproduce:

paste your core code

dbContextOptionsBuilder.UseMySql(config.DataConnectionString, option => {
option.CommandTimeout((int)TimeSpan.FromMinutes(3).TotalSeconds);
option.EnableRetryOnFailure();
option.MigrationsAssembly(typeof(DbContextOptionsBuilderExtensions).Assembly.FullName);
}).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);

my datetime colunm :
if (new List() { typeof(DateTime).Name }.Contains(type.Name))
{
return b.HasDefaultValueSql("'0000:00:00'");
}

Output:

System.InvalidCastException: Unable to cast object of type 'MySql.Data.Types.MySqlDateTime' to type 'System.DateTime'.
at EFCoreSecondLevelCacheInterceptor.EFTableRowsDataReader.GetDateTime(Int32 ordinal)
at Microsoft.EntityFrameworkCore.Query.Internal.BufferedDataReader.BufferedDataRecord.ReadDateTime(DbDataReader reader, Int32 ordinal, ReaderColumn column)
at Microsoft.EntityFrameworkCore.Query.Internal.BufferedDataReader.BufferedDataRecord.ReadRow()
at Microsoft.EntityFrameworkCore.Query.Internal.BufferedDataReader.BufferedDataRecord.InitializeAsync(DbDataReader reader, IReadOnlyList1 columns, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.BufferedDataReader.InitializeAsync(IReadOnlyList1 columns, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func4 operation, Func4 verifySucceeded, TState state, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func4 operation, Func4 verifySucceeded, TState state, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.AsyncEnumerator.MoveNextAsync()
System.InvalidCastException: Unable to cast object of type 'MySql.Data.Types.MySqlDateTime' to type 'System.DateTime'.
at EFCoreSecondLevelCacheInterceptor.EFTableRowsDataReader.GetDateTime(Int32 ordinal)
at Microsoft.EntityFrameworkCore.Query.Internal.BufferedDataReader.BufferedDataRecord.ReadDateTime(DbDataReader reader, Int32 ordinal, ReaderColumn column)
at Microsoft.EntityFrameworkCore.Query.Internal.BufferedDataReader.BufferedDataRecord.ReadRow()
at Microsoft.EntityFrameworkCore.Query.Internal.BufferedDataReader.BufferedDataRecord.InitializeAsync(DbDataReader reader, IReadOnlyList1 columns, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.BufferedDataReader.InitializeAsync(IReadOnlyList1 columns, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func4 operation, Func4 verifySucceeded, TState state, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func4 operation, Func4 verifySucceeded, TState state, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.AsyncEnumerator.MoveNextAsync()

Exception message:
Full Stack trace:

image
It is normal to add the following code

        if (value.GetType() != typeof(DateTime))
        {
            return DateTime.Parse(value.ToString(), CultureInfo.InvariantCulture);
        }

سوال در مورد FromSqlRaw

سلام وقت بخیر
من برای کش کردن خروجی از SqlRaw استفاده میکنم بدون مشکل کار میکنه
فقط بخش خروجی که در SP مشخص میشه مثل StatusCode اولین بار درست نمایش میده اما دفعات بعدی مقدار صفر نمایش میده اما اطلاعات رو از کش میخونه
آیا راهی هست که بخش Output در SP رو کش کنه؟ یا اصلا در این جور موارد نباید کش کرد؟

Cached item conflict with change tracker.

Hello. Face into an issue. Was investigate and found steps to reproduce.

Summary of the issue

Simple test to reproduce with any entity:

            var dbContext = ServiceProvider.GetRequiredService<DbContext>();
          
            await dbContext.Tags.AddAsync(new Tag()
            {
                Code = "123"
            });
            await dbContext.SaveChangesAsync();
          
            var tag = await dbContext.Tags.Cacheable().FirstOrDefaultAsync(x => x.Code == "123");
            dbContext.Attach(tag);

And you will get

The instance of entity type 'Tag' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.

If you remove Cachable() change tracker attach item without any problem.

How to not execute query on cache hit

Summary of the issue

If I enable the cache, SELECT query is not executed.
But, When I connected to DB, query(SET NAMES utf8mb4) is executing yet.
Is there the way to stop the query(SET NAMES utf8mb4)?

Environment

.NET Core SDK version: 3.1.7
Microsoft.EntityFrameworkCore version: 3.1.7
EFCoreSecondLevelCacheInterceptor version: 1.8.2
Pomelo.EntityFrameworkCore.MySql version: 3.1.2

Example code/Steps to reproduce:

startup.cs

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
 {
    const string inMemoryProviderName = "InMemoryQuerySecondCache";
    services.AddEFSecondLevelCache(options =>
        options.UseEasyCachingCoreProvider(inMemoryProviderName).DisableLogging(true)
    );

    services.AddEasyCaching(options =>
    {
        // use memory cache with your own configuration
        options.UseInMemory(config =>
        {
            config.DBConfig = new InMemoryCachingOptions
            {
                // scan time, default value is 60s
                ExpirationScanFrequency = 60,
                // total count of cache items, default value is 10000
                SizeLimit = 100,

                // enable deep clone when reading object from cache or not, default value is true.
                EnableReadDeepClone = false,
                // enable deep clone when writing object to cache or not, default value is false.
                EnableWriteDeepClone = false,
            };
            // the max random second will be added to cache's expiration, default value is 120
            config.MaxRdSecond = 120;
            // whether enable logging, default is false
            config.EnableLogging = false;
            // mutex key's alive time(ms), default is 5000
            config.LockMs = 5000;
            // when mutex key alive, it will sleep some time, default is 300
            config.SleepMs = 300;
        }, inMemoryProviderName);
    });

    services.AddDbContextPool<Data.Contexts.MyContext>(options =>
    {
        options.UseMySql(Configuration.GetConnectionString("MyConnectionString"), builder =>
        {
            builder.EnableRetryOnFailure(5, TimeSpan.FromSeconds(10), null);
        });
        options.UseLazyLoadingProxies();
        // FIXME: https://simpleinjector.readthedocs.io/en/latest/aspnetintegration.html
        options.AddInterceptors(services.BuildServiceProvider().GetRequiredService<SecondLevelCacheInterceptor>());
    }, poolSize: 50);
}
return context.MyTable.Where(x => x.foo = "bar")
                    .Cacheable(CacheExpirationMode.Sliding, TimeSpan.FromSeconds(30))
                    .FirstOrDefaultAsync();

Output:

MySQL general_log

2020-08-28T07:11:10.191480Z	   63 Query	SET NAMES utf8mb4
2020-08-28T07:11:10.192397Z	   63 Query	SET NAMES utf8mb4
2020-08-28T07:11:10.193733Z	   63 Query	SET NAMES utf8mb4
2020-08-28T07:11:10.194573Z	   63 Query	SET NAMES utf8mb4

Redis DefaultDataBase

class: RedisDbCache
Method: clearItems(string pattern = "")
var keys = string.IsNullOrWhiteSpace(pattern) ? server.Keys() : server.Keys(pattern: $"{pattern}");

if(DefaultDataBase != 0) can not get keys
Unable to enter foreach (var key in keys)

Here is the correct example
var keys = string.IsNullOrWhiteSpace(pattern) ? server.Keys(_connection.GetDatabase().Database) : server.Keys(_connection.GetDatabase().Database, pattern: $"{pattern}");

Cache not cleared

Summary of the issue

I have encountered this situation many times. When I add data and then read it, I cannot read it. I have to manually delete redis cache or wait for the cache to expire before reading it. When I encounter this problem, how can I find out what causes the cache not to be cleared.

Environment

.NET Core SDK version: 
Microsoft.EntityFrameworkCore version: 3.1
EFCoreSecondLevelCacheInterceptor version: 1.5.2

Example code/Steps to reproduce:

         var list = _context.Reports_temp.Where(p => p.CreateUserImid == imid).Cacheable(CacheExpirationMode.Sliding, new TimeSpan(0, 10, 0));

Output:

Clarification of usage on IQueryable

I am considering to use this library, but I would like to understand how to use it effectively. In the examples the queries are immediately executed, as it calls .FirstOrDefault. However I often use IQueryable and would like to understand at what point the data is cached.

for example:

IQueryable query = context.Cities.AsQueryable().AsNoTracking();
if (queryObj.name)
	query = query.Where(c => c.Name.Contains(queryObj.name));
// more filters can be applied on `query` depending on the values in `queryObj`

Then later on I will call the IQueryable with something like List<City> result = await query.ToListAsync(). Would I put Cacheable() on the call with AsQueryable or ToList? And what would be cached, the complete DbSet Cities, the results filtered for the specific value in queryObj.string, or maybe nothing at all because the query is different every time for new values in the multiple properties of queryObj?

cache all query doesnt work

hi
im config like this

  services.AddEFSecondLevelCache(options =>
        {
            options.UseMemoryCacheProvider(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(30))
                .DisableLogging(false)
                .CacheAllQueries(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(30));
        });

but it doesnt cache my queries im tracing with sql profile im faceing db request

when i use use the cacheable() for mu queries this neither doest work and have db request
but this "-- EFCachePolicy --> Absolute|00:30:00|||True" is apearing in queries

Error when using EFCoreSecondLevelCacheInterceptor

Summary of the issue

When adding the EF Cache Interceptor and using EF commands, I get this error:

Unable to resolve service for type 'EFCoreSecondLevelCacheInterceptor.IDbCommandInterceptorProcessor'

The DB has been configured and if I remove the "AddInterceptor" line all works.

What could be the reason for this error?

Environment

.NET Core SDK version: 3.1
Microsoft.EntityFrameworkCore version: 3.1.3
EFCoreSecondLevelCacheInterceptor version:  1.3.4

Example code/Steps to reproduce:

            var optionsBuilder = new DbContextOptionsBuilder<IdentityDbContext>();
            optionsBuilder.UseNpgsql(((IDbConfiguration) sp.GetService(typeof(IDbConfiguration))).ConnectionString);
            optionsBuilder.AddInterceptors(new SecondLevelCacheInterceptor());

Output:

Exception message:
Full Stack trace:
 Unable to resolve service for type 'EFCoreSecondLevelCacheInterceptor.IDbCommandInterceptorProcessor'. This is often because no database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext> object in its constructor and passes it to the base constructor for DbContext.
   at Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
   at EFCoreSecondLevelCacheInterceptor.SecondLevelCacheInterceptor.getProcessor(DbContext context)
   at EFCoreSecondLevelCacheInterceptor.SecondLevelCacheInterceptor.ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult`1 result, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Diagnostics.RelationalLoggerExtensions.CommandReaderExecutingAsync(IDiagnosticsLogger`1 diagnostics, IRelationalConnection connection, DbCommand command, DbContext context, Guid commandId, Guid connectionId, DateTimeOffset startTime, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken)
   at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)


Cache Policy Tag Performance Improvements

Cache Policy Tag Performance Improvements

Currently the second level cache interceptor leaves the -- EFCachPolicy tag in the executed sql. This has performance drawbacks that could be mitigated by a number of approaches.

For db providers that use query plan caching, the differences in the comments will cause failures to re-use existing query plans from the query plan cache. The comments also unnecessarily increase the size of the sql that needs to be sent.

Improvements:

  1. Remove the CallerLineNumber and CallerMemberName properties from the cache policy / cache policy tag. These properties are not referenced/used anywhere other than the ToString() method and are unnecessary. Removing these, particularly the line number, will eliminate query plan cache misses caused by the query coming from a different source code line.

  2. Remove the cache policy query tag from the command text in the ProcessExecutingCommands(..) method before continuing. The cache policy is parsed from this tag in these methods (e.g. ReaderExecutingAsync).

The parsed cache policy object can be held on to. For example a concurrent dictionary could be used with the DbCommand as the key and the EFCachePolicy object as the value. The tag can be removed from the command text. The ProcessExecutedCommands(..) methods can retrieve the parsed EFCachePolicy object from the dictionary and do not need the cache policy string to be parsed again.

Eliminating the comment from the executed sql will reduce the sql payload that needs to be sent and will eliminate query plan cache misses that would occur when the call site or cache policy properties are different from a previous query.

Environment

.NET Core SDK version: 3.1
Microsoft.EntityFrameworkCore version: 3.1.3
EFCoreSecondLevelCacheInterceptor version: 1.7.0

Example code/Steps to reproduce:

public T ProcessExecutingCommands<T>(DbCommand command, DbContext context, T result, [CallerMemberName] string methodName = null)
{
    var cachePolicy = _cachePolicyParser.GetEFCachePolicy(command.CommandText);
    if (cachePolicy != null)
    {
        if (_cachePolicyLookup.TryAdd(comand, cachePolicy)
        {
            command.CommandText = _cachePolicyParser.RemoveEFCachePolicyTag(command.CommandText);
        }

Output:

Exception message: N/A
Full Stack trace: N/A

CacheDependencies: EFUnknownsCacheDependency

Summary of the issue

Hello ,I use Pomelo.EntityFrameworkCore.MySql

Environment

.NET Core SDK version: 3.1
Microsoft.EntityFrameworkCore version:3.1.3
EFCoreSecondLevelCacheInterceptor version: 1.3.1

Example code/Steps to reproduce:

paste your core code

image

then code
_insideSquareBracketsOrQuotes.Matches(sql)

can't matche any tableName

image
image

Output:

info: EFCoreSecondLevelCacheInterceptor.EFCacheDependenciesProcessor[0]
It's not possible to calculate the related table names of the current query[SELECT EXISTS (
SELECT 1
FROM Navigation AS n
WHERE (n.Deleted = FALSE) AND ((LOWER(n.ItemUrl) = @__ToLower_0) AND n.IsList))]. Please use EFCachePolicy.Configure(options => options.CacheDependencies("real_table_name_1", "real_table_name_2")) to specify them explicitly.
info: EFCoreSecondLevelCacheInterceptor.EFCacheDependenciesProcessor[0]
ContextTableNames: Api_Route, Content, Department, Log, Menu, Menu_Auth_Mapping, Navigation, News, News_Type, Position, Request_Log, Role, Role_Api_Route_Mapping, Role_User_Mapping, Role_Web_Route_Mapping, Tenement, User_Department_Mapping, User_Info, User_Position_Mapping, Web_Route, PossibleQueryTableNames: -> CacheDependencies: EFUnknownsCacheDependency.

Exception message:
Full Stack trace:

Not possibe to set the Cacheable() method's settings globally

Summary of the issue

The documentation says it's possible to set the Cacheable() method's settings globally.

services.AddEFSecondLevelCache(options => options.UseMemoryCacheProvider(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(5)).DisableLogging(true));
var post1 = context.Posts
                   .Where(x => x.Id > 0)
                   .OrderBy(x => x.Id)
                   .Cacheable()
                   .FirstOrDefault();  // Async methods are supported too.

But the Cacheable() function is hardcoded to always use 30 minutes:
https://github.com/VahidN/EFCoreSecondLevelCacheInterceptor/blob/master/src/EFCoreSecondLevelCacheInterceptor/EFCachedQueryExtensions.cs#L164

What is the intended purpose of the UseMemoryCacheProvider(CacheExpirationMode expirationMode, TimeSpan timeout) overload?

ExpirationMode, Timeout priority when using CacheManager.Core

Summary of the issue

I have a question regarding the newly implemented support for CacheManager.Core.
Which ExpirationMode + Timeout is in effect when defined with both ConfigurationBuilder..WithExpiration() and Cacheable()?

In the example code below, which cache expiration is in effect for Customers?
Is it (1) Absolute for 10 Minutes or (2) Sliding for 5 Minutes?

Environment

.NET Core SDK version: 3.1
Microsoft.EntityFrameworkCore version:  3.1.3
EFCoreSecondLevelCacheInterceptor version: 1.5.1

Example code/Steps to reproduce:

// Startup.cs
services.AddEFSecondLevelCache(options =>
    options.UseCacheManagerCoreProvider().DisableLogging(true)
);

// Add an in-memory cache service provider
services.AddSingleton(typeof(ICacheManager<>), typeof(BaseCacheManager<>));
services.AddSingleton(typeof(ICacheManagerConfiguration),
    new CacheManager.Core.ConfigurationBuilder()
        .WithJsonSerializer()
        .WithMicrosoftMemoryCacheHandle(instanceName: "MyApplicationMemoryCache")
        .WithExpiration(ExpirationMode.Absolute, TimeSpan.FromMinutes(10))
        .Build());
var customers = await ApplicationDbContext.Customer
    .AsNoTracking()
    .Cacheable(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(5))
    .ToListAsync();

Output:

NA

Possibility of caching as dictionary or list

Summary of the issue

Hi, I've started using this library and I like it.
I have a question though.
If I'm not mistaken, it seems that only the result of the query is put in cache, which means that the real result of the linq query chain, often ending with ToDictionary(), ToList() is not put in cache.
And the creation of this structure can be expensive.
Would it be possible to include in the library something like AsCacheableDictionary(Async), AsCacheableList(Async), maybe also by prefixing the cachekey with the resulting type ?
Or do you think it's completely irrelevant and in that case, do you have an idea on how to achieve this ?

Thanks in advance,
Kindanam

Environment

.NET Core SDK version: 3.1
Microsoft.EntityFrameworkCore version: 3.1 
EFCoreSecondLevelCacheInterceptor version: 1.7.1 

Example code/Steps to reproduce:

var myDictionary = await myQuery.Cacheable(CacheExpirationMode.Absolute, TimeSpan.FromHours(1)).ToDictionaryAsync(myEntity => myEntity.Key);

// The hash code is different each time. it seems that the dictionary is always recreated and not itself put in cache
var hash = myDictionary.GetHashCode();

Output:

Exception message:
Full Stack trace:

Support for Net5.0

Hi,
This is more of a question than an issue.
With release of Net5.0 RC1 we are planning to do an upgrade. Is this package already compatible with Net5.0 and EFCore5.0 as it is right now?

Cache Only This Specific Entities List

Summary of the issue

Hi we are currently exploring the possibility to incorporate this library into our project, one scenario that came up,
due to the large query base, is the capability to specify a list of "cache this only" (e.g.: { "dbo.Entity1", "dbo.Entity2", ...})

so in combination with "cache all queries" configuration enabled, the interceptor would filter out all the other entities, rather than have to modify every single query manually(over 300+) to add .Cacheable()

Another reason why we would need something like this, we have implemented our entities hidden behind interfaces (the interceptor is DI passed with Autofac) and in a separated project from the main application in their own library, so adding the .Cacheable(), would cause the caching logic to leak in the context and potentially making the maintainability a nightmare in the future.

If this is actually already implemented please let me know cause I couldn't find it in the system

Environment

.NET Core SDK version: .Net Standard 2.1
Microsoft.EntityFrameworkCore version: 3.1.5
EFCoreSecondLevelCacheInterceptor version: 

muliple app use same context

hello there

thank u for ur incredible project

i have two separate application use same shared context

is there any way when the first app invalidate the cache the second app do it as well?

i will be thankfull if u help me

Fails to cache when used with other tags

Fails to cache when used with other tags

The EFCachePolicyParser.getParsedPolicy(..) logic is hard coded to only check the first line of the command text for the policy.

The -- EFCachePolicy tag is not guaranteed to be on the first line, and will fail to parse when used with other .TagWith(..) calls on the queryable.

var parts = firstLine.Split(new[] { EFCachePolicy.PartsSeparator }, StringSplitOptions.RemoveEmptyEntries);

The logic needs to be updated to account for other comments in the command text.

Environment

.NET Core SDK version: 3.1
Microsoft.EntityFrameworkCore version:  3.1.3
EFCoreSecondLevelCacheInterceptor version: 1.7.0

Example code/Steps to reproduce:

var allPeople = await context.Set<Person>()
    .TagWith("Our custom tags")
    .Cacheable()
    .ToListAsync(cancellationToken)
    .ConfigureAwait(false);

Output:

Exception message: N/A
Full Stack trace: N/A

Still send request to DB with cacheable query for Cosmos DB

Summary of the issue

I tried Cacheable and CacheAllQueries, with Absolute and Sliding expiration, but all the queries still send requests to DB.

Tracked by VS2019 debugger, the logger showed every query sent the request to DocDB.

Environment

.NET Core SDK version: 3.1.200
Microsoft.EntityFrameworkCore version: 3.1.2
EFCoreSecondLevelCacheInterceptor version: 1.1.2
DB: Azure Cosmos DB Emulator

Example code/Steps to reproduce:

optionsBuilder.UseCosmos(xxx, xxx, xxx);
optionsBuilder.AddInterceptors(new SecondLevelCacheInterceptor());
...
services.AddEFSecondLevelCache(opts => opts.UseMemoryCacheProvider());
...
var user = await context.Users
.Cacheable(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(10))
.FirstOrDefaultAsync(u => u.UserId == userId);

Output:

DocDB trace etc.

InvalidCastException: DateTime -> DateTimeOffset

Summary of the issue

Using PostgreSql with .Net Core 3.1. My entities use DateTimeOffset but after switching to this library from archived one, casting doesn't work.

Environment

.NET Core SDK version: 3.1.3 
Microsoft.EntityFrameworkCore version: 3.1.3
EFCoreSecondLevelCacheInterceptor version: 1.3.1 

Example code/Steps to reproduce:

same as your readme

Output:

An exception occurred while iterating over the results of a query for context type 'ApplicationDbContext'.
System.InvalidCastException: Unable to cast object of type 'System.DateTime' to type 'System.DateTimeOffset'.
   at System.Data.Common.DbDataReader.GetFieldValue[T](Int32 ordinal)
   at lambda_method(Closure , QueryContext , DbDataReader , ResultContext , Int32[] , ResultCoordinator )
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
System.InvalidCastException: Unable to cast object of type 'System.DateTime' to type 'System.DateTimeOffset'.
   at System.Data.Common.DbDataReader.GetFieldValue[T](Int32 ordinal)
   at lambda_method(Closure , QueryContext , DbDataReader , ResultContext , Int32[] , ResultCoordinator )
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.