Skip to content

Commit 996bdfe

Browse files
committed
Update caching docs for opt-in queries
Documents opt-in caching with marker interface/attribute, serializer preference selection, and updated defaults across reference, recipes, and package README.
1 parent 2fc86c3 commit 996bdfe

File tree

4 files changed

+51
-9
lines changed

4 files changed

+51
-9
lines changed

docs/extensions/caching.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,30 @@ builder.Services.AddMediatR(cfg =>
5252

5353
`QueryCachingBehavior<TRequest, TResponse>` applies cache-aside semantics:
5454

55-
- The default predicate caches request types whose names end with `Query` (case-insensitive).
55+
- The default predicate caches requests that opt in via `ICacheableQuery` or `[CacheableQuery]`.
5656
- The cache key uses the request type name as the resource and a SHA256 hash of the request payload.
5757
- Cache hits short-circuit the handler; cache misses store the handler result.
5858

59+
Opt-in a query by marker interface or attribute:
60+
61+
```csharp
62+
using CleanArchitecture.Extensions.Caching;
63+
using CleanArchitecture.Extensions.Caching.Abstractions;
64+
65+
[CacheableQuery]
66+
public record GetTodosQuery : IRequest<TodosVm>;
67+
68+
// or
69+
public record GetUserQuery(int Id) : IRequest<UserDto>, ICacheableQuery;
70+
```
71+
5972
Configure request selection and TTLs via `QueryCachingBehaviorOptions`:
6073

6174
```csharp
6275
builder.Services.AddCleanArchitectureCaching(
6376
configureQueryCaching: options =>
6477
{
65-
options.CachePredicate = request => request is ICacheableQuery; // your own marker interface
78+
options.CachePredicate = request => request is ICacheableQuery;
6679
options.DefaultTtl = TimeSpan.FromMinutes(2);
6780
options.TtlByRequestType[typeof(GetUserQuery)] = TimeSpan.FromSeconds(30);
6881
options.CacheNullValues = false;
@@ -75,7 +88,7 @@ builder.Services.AddCleanArchitectureCaching(
7588
- `DefaultCacheKeyFactory` hashes the request payload as JSON (deterministic SHA256).
7689
- `ICacheScope` supplies the namespace and optional tenant segment.
7790

78-
If you customize keys, keep them deterministic and stable across versions.
91+
If you customize keys, keep them deterministic and stable across versions. For user-scoped data, include user context in the hash or namespace.
7992

8093
## Choose a cache adapter
8194

@@ -110,6 +123,8 @@ builder.Services.AddSingleton<ICacheSerializer>(sp =>
110123
new SystemTextJsonCacheSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web)));
111124
```
112125

126+
When multiple serializers are registered, set `CachingOptions.PreferredSerializer` to a content type or serializer type name.
127+
113128
## Stampede protection and entry options
114129

115130
- `CachingOptions.StampedePolicy` controls locking, timeouts, and jitter.

docs/recipes/caching.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,20 @@ builder.Services.AddMediatR(cfg =>
4040
});
4141
```
4242

43-
4. Add invalidation for write operations (commands or domain events).
43+
4. Opt in queries to caching.
44+
45+
```csharp
46+
using CleanArchitecture.Extensions.Caching;
47+
using CleanArchitecture.Extensions.Caching.Abstractions;
48+
49+
[CacheableQuery]
50+
public record GetOrdersQuery : IRequest<OrdersVm>;
51+
52+
// or
53+
public record GetUserQuery(int Id) : IRequest<UserDto>, ICacheableQuery;
54+
```
55+
56+
5. Add invalidation for write operations (commands or domain events).
4457

4558
```csharp
4659
await cache.RemoveAsync(cacheScope.Create("GetOrdersQuery", hash));

docs/reference/caching-options.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
| `DefaultEntryOptions` | `CacheEntryOptions` | `CacheEntryOptions.Default` | Default entry options when no overrides are provided. |
1010
| `StampedePolicy` | `CacheStampedePolicy` | `CacheStampedePolicy.Default` | Locking and jitter defaults. |
1111
| `MaxEntrySizeBytes` | `long?` | `null` | Maximum payload size in bytes. |
12-
| `PreferredSerializer` | `string?` | `null` | Preferred serializer name/content type when multiple serializers are registered. |
12+
| `PreferredSerializer` | `string?` | `null` | Preferred serializer type name or content type when multiple serializers are registered. |
1313

1414
## QueryCachingBehaviorOptions
1515

1616
| Option | Type | Default | Description |
1717
| --- | --- | --- | --- |
18-
| `CachePredicate` | `Func<object,bool>` | Types ending with `Query` | Determines cacheable requests. |
18+
| `CachePredicate` | `Func<object,bool>` | `ICacheableQuery` or `[CacheableQuery]` | Determines cacheable requests. |
1919
| `ResourceNameSelector` | `Func<object,string>?` | `null` | Custom resource name for cache keys. |
2020
| `HashFactory` | `Func<object,string>?` | `null` | Custom hash for cache keys. |
2121
| `DefaultTtl` | `TimeSpan?` | `00:05:00` | Default TTL for cached queries. |
@@ -40,4 +40,3 @@
4040
| `EnableLocking` | `bool` | `true` | Enable per-key locking. |
4141
| `LockTimeout` | `TimeSpan` | `00:00:05` | Max wait for a lock. |
4242
| `Jitter` | `TimeSpan?` | `00:00:00.050` | Random jitter applied to expirations. |
43-
| `RefreshAhead` | `TimeSpan?` | `null` | Refresh-ahead interval (provider-dependent). |

src/CleanArchitecture.Extensions.Caching/README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,24 @@ builder.Services.AddMediatR(cfg =>
5959
});
6060
```
6161

62-
## Step 4 - What to expect
62+
## Step 4 - Opt-in queries
6363

64-
- Queries (types ending with `Query`) are cached by default; commands are not.
64+
Only queries that opt in are cached by default. Use the marker interface or attribute:
65+
66+
```csharp
67+
using CleanArchitecture.Extensions.Caching;
68+
using CleanArchitecture.Extensions.Caching.Abstractions;
69+
70+
[CacheableQuery]
71+
public record GetTodosQuery : IRequest<TodosVm>;
72+
73+
// or
74+
public record GetUserQuery(int Id) : IRequest<UserDto>, ICacheableQuery;
75+
```
76+
77+
## Step 5 - What to expect
78+
79+
- Only queries marked with `ICacheableQuery` or `[CacheableQuery]` are cached by default.
6580
- First request is a cache miss; the second identical request is a cache hit.
6681
- Default TTL is 5 minutes; override with `QueryCachingBehaviorOptions.DefaultTtl` or `TtlByRequestType`.
6782
- Debug logs show `Cache hit` and `Cache miss` messages when caching is active.

0 commit comments

Comments
 (0)