Add HybridCache.GetOrCreateAsync overloads for dynamic setting of options#129048
Merged
Conversation
…loads Introduce HybridCacheFactoryContext, a new type passed to the factory callback in GetOrCreateAsync, allowing the factory to dynamically influence cache entry options based on the result of the data fetch. The context exposes: - Expiration / LocalCacheExpiration: override entry lifetime (dotnet#121530) - Flags: override write-side cache flags, e.g. skip caching (dotnet#121530) - LocalCacheSize: set L1 cache entry size for MemoryCache SizeLimit (dotnet#114366) New virtual GetOrCreateAsync overloads mirror the existing key-shape family (string, ReadOnlySpan<char>, DefaultInterpolatedStringHandler) with both TState and no-TState variants. The default implementation wraps the context-aware factory and delegates to the existing abstract overload, preserving backward compatibility for existing HybridCache implementations. Implementations should override the new virtual method to honor context values. Contributes to dotnet#114366 Contributes to dotnet#121530 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add missing <summary> tag for LocalCacheExpiration and fix 'Gets or set' typo on Expiration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Clarify that DisableCompression is a write-side flag (honored from context) and DisableUnderlyingData is a read-side flag (ignored from context). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…entOptions and LocalCacheSize Implements API proposals in dotnet#114366 and dotnet#121530: - Rename HybridCacheFactoryContext to HybridCacheEntryContext. - Add HybridCacheEntryContext(HybridCacheEntryOptions currentOptions) constructor and CurrentOptions property. - Add LocalCacheSize init-only property to HybridCacheEntryOptions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
|
Tagging subscribers to this area: @dotnet/area-extensions-caching |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR expands Microsoft.Extensions.Caching.Hybrid.HybridCache with new GetOrCreateAsync overloads where the factory callback receives a mutable HybridCacheEntryOptions, enabling per-entry option adjustments at runtime (e.g., expiration, flags, local size). It also updates HybridCacheEntryOptions to support mutation tracking via Revision, adds LocalSize, and includes trimming and package-compat scaffolding for the new shape.
Changes:
- Make
HybridCacheEntryOptionsproperties mutable (get; set;), addLocalSize, and addRevisionmutation tracking (plus internal helpers). - Add new (virtual)
HybridCache.GetOrCreateAsyncoverloads whose factory receivesHybridCacheEntryOptions(string + NET-only key shapes). - Add trimming descriptor entries for internal helpers and add package compatibility suppressions for
init→setchanges.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/libraries/Microsoft.Extensions.Caching.Abstractions/src/Hybrid/HybridCacheEntryOptions.cs | Makes options mutable, adds LocalSize and Revision, plus internal clone/translation helpers. |
| src/libraries/Microsoft.Extensions.Caching.Abstractions/src/Hybrid/HybridCache.cs | Introduces options-aware GetOrCreateAsync overloads and a default implementation bridging to the existing abstract overload. |
| src/libraries/Microsoft.Extensions.Caching.Abstractions/src/ILLink/ILLink.Descriptors.LibraryBuild.xml | Preserves internal members accessed cross-assembly via UnsafeAccessor for trimming. |
| src/libraries/Microsoft.Extensions.Caching.Abstractions/src/CompatibilitySuppressions.xml | Adds package validation suppressions for the accessor-shape change. |
| src/libraries/Microsoft.Extensions.Caching.Abstractions/ref/Microsoft.Extensions.Caching.Abstractions.cs | Updates the public API contract for HybridCacheEntryOptions and adds the new string-key overloads. |
| src/libraries/Microsoft.Extensions.Caching.Abstractions/ref/Microsoft.Extensions.Caching.Abstractions.net10.cs | Adds NET-only key-shape overloads for the new factory signature. |
This was referenced Jun 5, 2026
Open
… overloads Cover the new context-aware GetOrCreateAsync overloads for the ReadOnlySpan<char> and DefaultInterpolatedStringHandler key shapes (previously only string keys were tested): the factory receives a context and its mutations reach the cache write, and for the interpolated-string handler the key is cleared on both the async (factory ran) and synchronous (cache hit) completion paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The type-level remarks implied the properties are always initialized to the effective values for the operation. That only holds when a HybridCache implementation seeds the context for a factory callback; a directly constructed context reflects only the options passed to its constructor. Clarify the remarks accordingly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the four explicit backing fields with the field keyword in each property setter. The constructor now seeds through the setters and resets Revision to zero so a freshly seeded context still reports no caller changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The properties are init-only but still settable by the caller at construction, so the documentation should follow the standard 'Gets or sets' phrasing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…in default GetOrCreateAsync - Create/seed the HybridCacheEntryContext lazily inside the wrapped factory delegate, so a cache hit (where the factory never runs) no longer allocates a context. The DefaultImplState now carries the original options and receives the context back (a non-null context signals the factory ran, replacing the FactoryRan flag). - Reuse a shared immutable write-disabled options instance for the common options == null case instead of cloning per call. - Only materialize new options for the trailing SetAsync when the factory actually mutated the context (Revision != 0); otherwise reuse the caller's original options, avoiding an allocation and preserving null-options semantics that implementations may treat specially. - Add a contract test covering the unchanged-context path preserving null options. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
svick
marked this pull request as ready for review
July 15, 2026 12:33
|
Azure Pipelines: Successfully started running 3 pipeline(s). 12 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
3 tasks
rosebyte
approved these changes
Jul 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the API proposals approved in #121530 and #114366.
There is a companion PR at dotnet/extensions#7568.
Important
The public
HybridCacheEntryContextconstructor (see below) is awaiting API review approval — it is not part of the currently approved API, which specifies aninternalconstructor. See the rationale under HybridCacheEntryContext.Changes
HybridCacheEntryOptions (immutable + LocalSize)
Expiration,LocalCacheExpiration, andFlagsremaininit-only (immutable), as originally shipped.LocalSizeproperty (init) to control the size assigned to the local (L1) cache entry (e.g.MemoryCache.Size).HybridCacheEntryContext (mutable factory view)
New
sealedtype handed to the options-aware factory callbacks so they can influence cache entry options based on the produced value:Expiration,LocalCacheExpiration,Flags, andLocalSize(seeded from the effective options).Revisionproperty that increments on each property change, allowing implementations to detect whether the factory changed anything.HybridCacheEntryContext(HybridCacheEntryOptions?)constructor that seeds the context from the effective options, so third-partyHybridCacheimplementations can construct and populate a context in one line. The approved API specifies aninternalconstructor; making it public is awaiting API review approval.HybridCache (context-aware factory overloads)
New virtual
GetOrCreateAsyncoverloads that take aFunc<…, HybridCacheEntryContext, CancellationToken, ValueTask<T>>factory, mirroring the existing key-shape family (string,ReadOnlySpan<char>,DefaultInterpolatedStringHandler) with bothTStateand no-TStatevariants.The default implementation seeds a
HybridCacheEntryContextfrom the effective options, invokes the factory with local/distributed writes suppressed, and then performs a singleSetAsyncusing the (possibly factory-mutated) options — preserving backward compatibility for existingHybridCacheimplementations. Implementations should override the new virtual to honor factory-supplied values natively (usingRevisionfor mutation detection).Tests
Added contract tests for the default
GetOrCreateAsyncimplementation, exercised against a fake in-memoryHybridCacheso any correct default implementation passes.Note
This PR description was updated with the assistance of GitHub Copilot.