Overview
Silky's cache interceptor provides an AOP-based transparent caching mechanism: annotate attributes on service interface methods and the framework automatically handles cache reads, updates, and deletions — no caching logic needed in business code.
The cache interceptor uses IDistributedInterceptCache (backed by Redis by default), implemented via CachingInterceptor (a SilkyInterceptor) that intercepts service method calls.
Enabling Cache Interception
Two conditions must both be true:
Governance:EnableCachingInterceptor = true(the default)- At least one caching attribute is annotated on the method
To disable cache interception for a specific method:
[Governance(EnableCachingInterceptor = false)]
Task<AccountDto> GetSensitiveDataAsync(long id);
Cache Intercept Attributes
[GetCachingIntercept] — Cache Read
[GetCachingIntercept("{id}")]
Task<AccountDto> GetByIdAsync(long id);
Behavior: Check cache first; return cached value on hit without executing the method. On cache miss, execute the method and write the result to cache.
| Property | Default | Description |
|---|---|---|
KeyTemplate (constructor) | — | Cache key template using {paramName} placeholders referencing method parameters |
OnlyCurrentUserData | false | When true, appends the current user ID to the cache key (user-level isolation) |
IgnoreMultiTenancy | false | When true, ignores tenant isolation — different tenants share the same cache |
[UpdateCachingIntercept] — Cache Update
[UpdateCachingIntercept("{id}")]
Task<AccountDto> UpdateAsync(long id, UpdateAccountInput input);
Behavior: Execute the method, then overwrite the cache entry with the new return value (SetAsync). Supports multiple [UpdateCachingIntercept] attributes on the same method (AllowMultiple = true).
| Property | Default | Description |
|---|---|---|
KeyTemplate (constructor) | — | Cache key template |
OnlyCurrentUserData | false | User-level data isolation |
IgnoreMultiTenancy | false | Tenant isolation control |
IgnoreWhenCacheKeyNull | true | When true, skip the cache operation if the resolved key is null; when false, throw an exception |
[RemoveCachingIntercept] — Cache Invalidation
// Specify cache partition by string name
[RemoveCachingIntercept("Account", "{id}")]
Task DeleteAsync(long id);
// Specify cache partition by type (uses type's short name)
[RemoveCachingIntercept(typeof(GetCachingInterceptAttribute), "{id}")]
Task DeleteAsync(long id);
Behavior: Execute the method, then delete the matching cache entry by CacheName + KeyTemplate. Supports multiple attributes.
| Property | Default | Description |
|---|---|---|
CacheName (constructor) | — | Cache partition name (similar to Redis Hash key) |
KeyTemplate (constructor) | — | Cache key template |
OnlyCurrentUserData | false | User-level data isolation |
IgnoreMultiTenancy | false | Tenant isolation control |
[RemoveMatchKeyCachingIntercept] — Pattern-Match Cache Invalidation
// Delete all cache keys starting with "Account:"
[RemoveMatchKeyCachingIntercept("Account", "Account:*")]
Task BatchUpdateAsync(BatchUpdateInput input);
Behavior: Uses a wildcard/regex pattern to find and delete all matching cache keys. Suitable for batch operations that invalidate multiple related cache entries.
Key Template Placeholders
Cache keys are generated from KeyTemplate by substituting placeholders at runtime:
Parameter Placeholders
// {paramName} — replaced by the value of the method parameter named 'paramName'
[GetCachingIntercept("{id}")]
Task<AccountDto> GetByIdAsync(long id);
// Key: "AccountDto:{id_value}"
Object Property Placeholders ([HashKey])
When a parameter is a complex object, mark specific properties with [HashKey] to include them in the key:
public class GetAccountInput
{
[HashKey]
public long Id { get; set; }
public string Name { get; set; } // not included in cache key
}
[GetCachingIntercept("{input}")]
Task<AccountDto> GetAsync(GetAccountInput input);
// Key built from: input.Id (the [HashKey] property)
Multi-Tenant & User Isolation
Cache keys are automatically prefixed based on isolation settings:
| Setting | Key Prefix |
|---|---|
| Default (multi-tenant enabled) | {TenantId}:{CacheName}:{KeyTemplate} |
IgnoreMultiTenancy = true | {CacheName}:{KeyTemplate} |
OnlyCurrentUserData = true | {TenantId}:{UserId}:{CacheName}:{KeyTemplate} |
Interaction with Distributed Transactions
Inside a TCC distributed transaction, the Try phase skips cache reads (always executes the method) and defers cache writes until the Confirm phase. The Cancel phase removes any cache entries that were tentatively written during Try.
This prevents a compensated (cancelled) operation from polluting the cache with stale data.
