Skip to content

Commit 179ca12

Browse files
committed
feat: add cache management methods for invalidating and clearing cached results in StoreComponentWithUtilities
1 parent 02db976 commit 179ca12

4 files changed

Lines changed: 83 additions & 30 deletions

File tree

README.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -363,17 +363,24 @@ var user = await LazyLoad($"user-{userId}", () => UserService.GetUserAsync(userI
363363

364364
#### Cache Invalidation
365365

366-
Control cached entries when data changes:
366+
Control cached entries when data changes (in components inheriting `StoreComponentWithUtilities<TState>`):
367367

368368
```csharp
369369
// Remove specific cached entry
370-
executor.InvalidateCache($"product-{productId}");
370+
InvalidateCachedResult($"product-{productId}");
371371

372372
// Remove all entries with prefix (e.g., after bulk operation)
373-
executor.InvalidateCacheByPrefix("product-");
373+
InvalidateCachedResultsByPrefix("product-");
374374

375375
// Clear all cached results (e.g., on user logout)
376-
executor.ClearCache();
376+
ClearCachedResults();
377+
```
378+
379+
Or access the executor directly:
380+
```csharp
381+
AsyncExecutor?.InvalidateCache($"product-{productId}");
382+
AsyncExecutor?.InvalidateCacheByPrefix("product-");
383+
AsyncExecutor?.ClearCache();
377384
```
378385

379386
> **Note:** Only the first caller's callbacks (loading, success, error) are executed. Concurrent callers receive the same result but their callbacks are NOT invoked. This is intentional for deduplication.

docs-site/api-reference.html

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -918,14 +918,18 @@ <h4><code>ExecuteCachedAsync&lt;TResult&gt;</code></h4>
918918
</div>
919919

920920
<h3>Cache Management Methods</h3>
921+
<p>These methods are available on <code>IAsyncActionExecutor&lt;TState&gt;</code>. Components inheriting from <code>StoreComponentWithUtilities&lt;TState&gt;</code> also have convenience wrapper methods: <code>InvalidateCachedResult()</code>, <code>InvalidateCachedResultsByPrefix()</code>, and <code>ClearCachedResults()</code>.</p>
921922

922923
<div class="api-section">
923924
<h4><code>InvalidateCache(string cacheKey)</code></h4>
924925
<p>Removes a specific cached result by key. Use this when you know the exact cache key to invalidate.</p>
926+
<p><strong>Component wrapper:</strong> <code>InvalidateCachedResult(cacheKey)</code></p>
925927
<div class="code-block">
926-
<pre><code class="language-csharp">// After updating a product, invalidate its cache
927-
await productService.UpdateProductAsync(product);
928-
asyncExecutor.InvalidateCache($"product-{productId}");</code></pre>
928+
<pre><code class="language-csharp">// In a component (using wrapper method)
929+
InvalidateCachedResult($"product-{productId}");
930+
931+
// Or using executor directly
932+
AsyncExecutor?.InvalidateCache($"product-{productId}");</code></pre>
929933
</div>
930934
<p><strong>Behavior:</strong></p>
931935
<ul>
@@ -938,10 +942,10 @@ <h4><code>InvalidateCache(string cacheKey)</code></h4>
938942
<div class="api-section">
939943
<h4><code>InvalidateCacheByPrefix(string prefix)</code></h4>
940944
<p>Removes all cached results with keys starting with the specified prefix. Useful for invalidating related cache entries.</p>
945+
<p><strong>Component wrapper:</strong> <code>InvalidateCachedResultsByPrefix(prefix)</code></p>
941946
<div class="code-block">
942-
<pre><code class="language-csharp">// After adding a new product, invalidate all product caches
943-
await productService.CreateProductAsync(newProduct);
944-
asyncExecutor.InvalidateCacheByPrefix("product-");
947+
<pre><code class="language-csharp">// In a component (using wrapper method)
948+
InvalidateCachedResultsByPrefix("product-");
945949

946950
// Invalidates: "product-1", "product-2", "product-list", etc.</code></pre>
947951
</div>
@@ -967,10 +971,13 @@ <h4><code>InvalidateCacheByPrefix(string prefix)</code></h4>
967971
<div class="api-section">
968972
<h4><code>ClearCache()</code></h4>
969973
<p>Clears all cached results. Use this sparingly, typically only for global state resets.</p>
974+
<p><strong>Component wrapper:</strong> <code>ClearCachedResults()</code></p>
970975
<div class="code-block">
971-
<pre><code class="language-csharp">// Clear all caches on logout
972-
await authService.LogoutAsync();
973-
asyncExecutor.ClearCache();</code></pre>
976+
<pre><code class="language-csharp">// In a component (using wrapper method)
977+
ClearCachedResults();
978+
979+
// Or using executor directly
980+
AsyncExecutor?.ClearCache();</code></pre>
974981
</div>
975982
<p><strong>When to use:</strong></p>
976983
<ul>
@@ -994,23 +1001,23 @@ <h4>Cache Management Best Practices</h4>
9941001
<tbody>
9951002
<tr>
9961003
<td>Single item updated</td>
997-
<td><code>InvalidateCache("item-{id}")</code></td>
1004+
<td><code>InvalidateCachedResult("item-{id}")</code></td>
9981005
</tr>
9991006
<tr>
10001007
<td>Multiple related items changed</td>
1001-
<td><code>InvalidateCacheByPrefix("category-")</code></td>
1008+
<td><code>InvalidateCachedResultsByPrefix("category-")</code></td>
10021009
</tr>
10031010
<tr>
10041011
<td>User logout</td>
1005-
<td><code>ClearCache()</code></td>
1012+
<td><code>ClearCachedResults()</code></td>
10061013
</tr>
10071014
<tr>
10081015
<td>List item added/removed</td>
1009-
<td><code>InvalidateCacheByPrefix("list-")</code></td>
1016+
<td><code>InvalidateCachedResultsByPrefix("list-")</code></td>
10101017
</tr>
10111018
<tr>
10121019
<td>Settings changed</td>
1013-
<td><code>InvalidateCacheByPrefix("settings-")</code></td>
1020+
<td><code>InvalidateCachedResultsByPrefix("settings-")</code></td>
10141021
</tr>
10151022
</tbody>
10161023
</table>
@@ -1125,15 +1132,15 @@ <h2 id="quick-reference">Quick Reference</h2>
11251132
<td>Validate state integrity</td>
11261133
</tr>
11271134
<tr>
1128-
<td><code>InvalidateCache()</code></td>
1135+
<td><code>InvalidateCachedResult()</code></td>
11291136
<td>Remove specific cached result</td>
11301137
</tr>
11311138
<tr>
1132-
<td><code>InvalidateCacheByPrefix()</code></td>
1139+
<td><code>InvalidateCachedResultsByPrefix()</code></td>
11331140
<td>Remove cached results by prefix</td>
11341141
</tr>
11351142
<tr>
1136-
<td><code>ClearCache()</code></td>
1143+
<td><code>ClearCachedResults()</code></td>
11371144
<td>Clear all cached results</td>
11381145
</tr>
11391146
</tbody>

docs-site/async-helpers/execute-cached-async.html

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ <h2>API Reference</h2>
240240
</table>
241241

242242
<h3>Cache Invalidation Methods</h3>
243+
<p>Available in components inheriting <code>StoreComponentWithUtilities&lt;TState&gt;</code>:</p>
243244

244245
<table class="table">
245246
<thead>
@@ -250,19 +251,21 @@ <h3>Cache Invalidation Methods</h3>
250251
</thead>
251252
<tbody>
252253
<tr>
253-
<td><code>InvalidateCache(string cacheKey)</code></td>
254+
<td><code>InvalidateCachedResult(string cacheKey)</code></td>
254255
<td>Removes a specific cache entry by key. Use after edit/delete operations to force fresh data on next call.</td>
255256
</tr>
256257
<tr>
257-
<td><code>InvalidateCacheByPrefix(string prefix)</code></td>
258-
<td>Removes all cache entries matching the prefix. Example: <code>InvalidateCacheByPrefix("product-")</code> clears all product caches.</td>
258+
<td><code>InvalidateCachedResultsByPrefix(string prefix)</code></td>
259+
<td>Removes all cache entries matching the prefix. Example: <code>InvalidateCachedResultsByPrefix("product-")</code> clears all product caches.</td>
259260
</tr>
260261
<tr>
261-
<td><code>ClearCache()</code></td>
262+
<td><code>ClearCachedResults()</code></td>
262263
<td>Removes all cache entries. Use on logout or when global state reset is needed.</td>
263264
</tr>
264265
</tbody>
265266
</table>
267+
268+
<p class="text-muted">You can also access the executor directly via <code>AsyncExecutor?.InvalidateCache()</code>, <code>AsyncExecutor?.InvalidateCacheByPrefix()</code>, and <code>AsyncExecutor?.ClearCache()</code>.</p>
266269
</section>
267270

268271
<!-- When to Use -->
@@ -427,7 +430,7 @@ <h2>Cache Invalidation</h2>
427430
await ProductApi.UpdateAsync(product);
428431

429432
// Force fresh data on next ExecuteCachedAsync call
430-
InvalidateCache($"product-{product.Id}");
433+
InvalidateCachedResult($"product-{product.Id}");
431434

432435
// Next component that calls ExecuteCachedAsync will fetch fresh data
433436
await ExecuteCachedAsync(
@@ -444,10 +447,10 @@ <h2>Cache Invalidation</h2>
444447
await ProductApi.BulkUpdateAsync(products);
445448

446449
// Clear all product-related caches
447-
InvalidateCacheByPrefix("product-");
450+
InvalidateCachedResultsByPrefix("product-");
448451

449452
// Or clear category-specific caches
450-
InvalidateCacheByPrefix($"category-{categoryId}-products");</code></pre>
453+
InvalidateCachedResultsByPrefix($"category-{categoryId}-products");</code></pre>
451454
</div>
452455

453456
<div class="code-block">
@@ -458,7 +461,7 @@ <h2>Cache Invalidation</h2>
458461
await AuthApi.LogoutAsync();
459462

460463
// Clear all cached data
461-
ClearCache();
464+
ClearCachedResults();
462465

463466
// Reset state
464467
await UpdateAsync(_ => AppState.Initial);
@@ -469,7 +472,7 @@ <h2>Cache Invalidation</h2>
469472
// On global state reset
470473
public async Task ResetApplicationAsync()
471474
{
472-
ClearCache();
475+
ClearCachedResults();
473476
await UpdateAsync(_ => AppState.Initial);
474477
}</code></pre>
475478
</div>

src/EasyAppDev.Blazor.Store/Blazor/StoreComponentWithUtilities.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,42 @@ protected Task ExecuteCachedAsync<TResult>(
356356
return AsyncExecutor.ExecuteCachedAsync(cacheKey, asyncAction, loading, success, error, cacheFor, cancellationToken, action);
357357
}
358358

359+
/// <summary>
360+
/// Invalidates a cached result from ExecuteCachedAsync by its cache key.
361+
/// </summary>
362+
/// <param name="cacheKey">The cache key to invalidate.</param>
363+
/// <remarks>
364+
/// This only affects cached results, not in-flight operations.
365+
/// The next call to ExecuteCachedAsync with this key will execute the async action again.
366+
/// </remarks>
367+
protected void InvalidateCachedResult(string cacheKey)
368+
{
369+
AsyncExecutor?.InvalidateCache(cacheKey);
370+
}
371+
372+
/// <summary>
373+
/// Invalidates all cached results from ExecuteCachedAsync with keys starting with the specified prefix.
374+
/// </summary>
375+
/// <param name="prefix">The prefix to match cache keys against.</param>
376+
/// <remarks>
377+
/// Useful for invalidating related cache entries (e.g., "product-" invalidates "product-1", "product-2", etc.).
378+
/// </remarks>
379+
protected void InvalidateCachedResultsByPrefix(string prefix)
380+
{
381+
AsyncExecutor?.InvalidateCacheByPrefix(prefix);
382+
}
383+
384+
/// <summary>
385+
/// Clears all cached results from ExecuteCachedAsync.
386+
/// </summary>
387+
/// <remarks>
388+
/// This only affects cached results, not in-flight operations.
389+
/// </remarks>
390+
protected void ClearCachedResults()
391+
{
392+
AsyncExecutor?.ClearCache();
393+
}
394+
359395
/// <summary>
360396
/// Loads data with automatic caching and request deduplication.
361397
/// </summary>

0 commit comments

Comments
 (0)