Cache Interception
Silky uses cache interception to reduce network IO during RPC calls and improve distributed system performance. Cache interception is enabled by default:
governance:
enableCachingInterceptor: true # default: true; set false to disable globally
Three types of cache attributes are supported — for reading, updating, and removing cached data.
GetCachingIntercept — Read Cache
During an RPC call, if the cache key hits an entry in the distributed cache, data is returned directly without a network request. On a miss, the call proceeds normally and the result is written to the cache for future calls.
[GetCachingIntercept("name:{Name}")]
Task<TestOut> Create(TestInput testInput);
The template {Name} is automatically resolved to the value of the Name property on the input parameter. The effective cache key is derived from: template + resolved parameter values + return type.
OnlyCurrentUserData = true adds the current user's ID to the cache key — useful when the cached data is user-specific:
[GetCachingIntercept("userId:{UserId}", OnlyCurrentUserData = true)]
Task<UserProfileOutput> GetProfileAsync(long userId);
Warning
When OnlyCurrentUserData = true, ensure the endpoint requires authentication.
UpdateCachingIntercept — Update Cache
After a write operation, use UpdateCachingIntercept to keep the cache consistent with the database:
[UpdateCachingIntercept("name:{Name}")]
Task<TestOut> Update(UpdateTestInput input);
Unlike GetCachingIntercept, the method always executes; the result is then written to the cache.
RemoveCachingIntercept — Remove Cache
Remove a specific cache entry after a delete or destructive update:
[RemoveCachingIntercept("ITestAppService", "name:{Name}")]
Task Delete(DeleteTestInput input);
RemoveMatchKeyCachingIntercept — Remove by Pattern
Remove all cache entries whose keys match a pattern (useful after bulk updates):
[RemoveMatchKeyCachingIntercept("ITestAppService", "name:")]
Task BatchDelete(BatchDeleteInput input);
Redis Cache Configuration
PM> Install-Package Silky.Caching.StackExchangeRedis -Version 3.9.2
redis:
configuration: "127.0.0.1:6379,password=yourpassword"
distributedCache:
keyPrefix: "myapp:" # optional global key prefix
hideErrors: true # suppress cache errors (return null instead of throwing)
defaultSlidingExpireTime: "00:20:00" # 20-minute sliding expiry
IDistributedCache<T> API
For programmatic cache operations, inject IDistributedCache<TCacheItem>:
public class ProductAppService : IProductAppService, IScopedDependency
{
private readonly IDistributedCache<ProductOutput, long> _cache;
public ProductAppService(IDistributedCache<ProductOutput, long> cache)
{
_cache = cache;
}
public async Task<ProductOutput> GetAsync(long id)
{
return await _cache.GetOrAddAsync(id,
async () =>
{
// Factory: called only on cache miss
var product = await _productRepository.FindAsync(id);
return product.MapTo<ProductOutput>();
},
() => new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30)
});
}
}
Key methods:
| Method | Description |
|---|---|
GetAsync(key) | Get a cached item by key |
SetAsync(key, value, options) | Set a cache entry with optional expiry |
GetOrAddAsync(key, factory, options) | Get or create (thread-safe, factory runs once on miss) |
RemoveAsync(key) | Remove a specific cache entry |
GetManyAsync(keys) | Batch get multiple entries |
SetManyAsync(pairs, options) | Batch set multiple entries |
