/CachedQueries

A library provides IQueryable results caching

Primary LanguageC#MIT LicenseMIT

CachedQueries: Efficient Caching for Entity Framework Queries

License NuGet version (CachedQueries) NuGet downloads (CachedQueries) Build status Coverage Status CodeFactor

Introduction

CachedQueries is an open-source .NET library for adding efficient caching to Entity Framework queries. It simplifies caching IQueryable results by enabling caching directly within EF, thus eliminating the need for extra abstraction layers over DbSet.

Getting Started

  1. Install package
dotnet add package CachedQueries
  1. Dependency Injection Setup (example using memory cache):
// Setup system cache
services.AddMemoryCache();

// Add CachedQueries to your services
services.AddQueriesCaching(options =>
    options
        .UseCacheStore(MemoryCache)
        .UseEntityFramework());

// Use CachedQueries in your application
app.UseQueriesCaching();
  1. Cache Invalidation Integration
// Extend SaveChanges and SaveChangesAsync methods in EF context
public override async Task<int> SaveChangesAsync(CancellationToken token = default)
{
    // Invoke cache expiration
    await ChangeTracker.ExpireEntitiesCacheAsync(token);
    return await base.SaveChangesAsync(token);
}

Basic Usage

Cache collection

Easily cache collections, including related data:

// Standard caching
var results = await context.Blogs
    .Include(x => x.Posts)
    .ToCachedListAsync(cancellationToken);

// Caching with expiration
var results = await context.Posts
    .ToCachedListAsync(TimeSpan.FromHours(8), cancellationToken);

// Caching with custom tags
var results = await context.Posts
    .ToCachedListAsync(TimeSpan.FromHours(8), new List<string> { "custom_tag" }, cancellationToken);

Caching Individual Items

Cache single entities:

// Cache a single entity based on a condition
var result = await context.Blogs
    .Include(x => x.Posts)
    .CachedFirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken);

// Cache an entity with a predetermined expiration time
var result = await context.Posts
    .CachedFirstOrDefaultAsync(TimeSpan.FromHours(8), cancellationToken);

// Cache using custom tags for nuanced control
var result = await context.Posts
    .CachedFirstOrDefaultAsync(TimeSpan.FromHours(8), new List<string> { "custom_tag" }, cancellationToken);

Invalidating Cache

CachedQueries efficiently handles cache invalidation, maintaining data accuracy and relevance.

Auto Invalidation

To integrate automatic cache invalidation within CachedQueries, it is efficient to override the EF context SaveChanges and SaveChangesAsync methods.

public override async Task<int> SaveChangesAsync(CancellationToken token = default)
{
    await ChangeTracker.ExpireEntitiesCacheAsync(token);
    return base.SaveChangesAsync(token);
}

Manual Invalidation Using Custom Tags

Control cache updates in case using custom tags:

// Setup the cache invalidator (typically done during initialization)
private ICacheInvalidator _cacheInvalidator;
...
// Invalidate specific cache segments by custom tag
_cacheInvalidator.InvalidateCacheAsync(new List<string> { "custom_tag" })

Use Redis distributed Cache

Use Redis for scalable, distributed caching:

// Setup distributed cache
services.AddDistributedCache();
// Setup Redis
services.AddStackExchangeRedisCache(config);

// Add CachedQueries to your services
services.AddQueriesCaching(options =>
    options
        .UseCacheStore(DistributedCache)
        .UseLockManager<RedisLockManager>()
        .UseEntityFramework());

// Use CachedQueries in your application
app.UseQueriesCaching();

More Customization Options

Customize key aspects for specific needs:

  • Cache Key Factory: Allows for the implementation of unique logic in generating cache keys.
  • Lock Manager: Customization of concurrent access and locking strategies for cache entries.
  • Cache Options: Enables the setting and adjustment of global cache settings.
  • Cache Invalidator: Provides the capability to devise specific rules for invalidating cache entries.

Custom Dependency Injection example:

services.AddQueriesCaching(options =>
    options
    .UseOptions(new CacheOptions {
        LockTimeout = TimeSpan.FromSeconds(10),
        DefaultExpiration = TimeSpan.FromMinutes(30)
    })
    .UseCacheStore(CustomCacheStore) // ICacheStore implementation
    .UseCacheStoreProvider(CustomCacheStoreProvider) // ICacheStoreProvider implementation
    .UseCacheInvalidator(CustomCacheInvalidator) // ICacheInvalidator implementation
    .UseLockManager(CustomLockManager) // ILockManager implementation
    .UseKeyFactory(CustomKeyFactory) // ICacheKeyFactory implementation
    .UseEntityFramework()); // Integration with Entity Framework

// Activation of CachedQueries in applications
app.UseQueriesCaching();

Conclusion

Discover more about CachedQueries through the library's test cases, offering insights into detailed functionalities and advanced usage examples.