[Validation] CacheView vary-by dimensions
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 290
Description
**Scenario contact:** @dariatiurina
## Scenario
A cached section is shared by every request that maps to the same key, so anything the section's output depends on has to be declared: `VaryByQuery`, `VaryByRoute`, `VaryByHeader`, `VaryByCookie`, `VaryByUser`, `VaryByCulture`, or a custom `VaryBy` string the app computes itself.
Validate that each dimension really splits the cache and that identical requests still hit. Include a controlled example of ordinary cacheable content missing a vary-by dimension, so its effect is visible without assuming every component has the same caching policy.
## Minimum build
.NET 11 RC1.
## Configurations to cover
* Blazor Web App
* [x] Static SSR
* [ ] Interactive Server
* [ ] Interactive WebAssembly
* [ ] Interactive Auto
* [ ] Standalone WebAssembly
* [ ] Hybrid (MAUI)
## Also exercise
* [ ] Published output
* [ ] An existing .NET 10 app upgraded to .NET 11
* [ ] Trimming or ahead-of-time compilation
* [ ] More than one server instance, or a proxy in front
* [ ] Hot Reload
* [ ] An IDE as well as the command line
* [ ] Container
* [x] N/A
## Setup
Cookie authentication with two test users, request localization with at least two cultures, and a route with a `{region}` segment. Use two browser profiles or `curl` with separate cookie jars so identities cannot mix by accident. Set a cache lifetime long enough to complete the comparisons without expiry, and keep the route and culture providers independent when changing only one dimension.
## What to build
One page at `/{region}/demo` with a `CacheView` for each dimension. Each contains a cacheable child that creates a `Guid` during initialization and displays the value it varied on, so a wrong entry is obvious in the page rather than something to deduce.
Include two query sections: one varying by two named parameters, one varying by `*`. Include a section varying by a value the app computes at request time (an A/B bucket) and passes to `VaryBy`.
Then add an ordinary, unannotated cacheable child that reads and displays a synthetic test user's name, inside a section with no variation. Do not use `AuthorizeView` or a child marked to rerender for this control: those have different caching policies.
## Things to try
* For each dimension: request the same URL twice unchanged, then twice with only that dimension changed.
* Query: change a declared parameter, an undeclared one, and the order of distinct parameter names. Separately try repeated values and changes to name/value casing, recording those results without assuming all forms of reordering are equivalent. Compare the named section against the `*` section on the same requests.
* Route: request `/de/demo` and `/fr/demo`.
* Cookie: change the cookie, then delete it.
* Header: change the declared header, send it twice with different values, omit it.
* User: load as user A, then as user B in a separate profile, then signed out.
* Culture: send different `Accept-Language` values.
* Custom: flip the A/B bucket for the same URL and user.
* Change two dimensions at once, then change them back, and check the original entry is still there.
* Load the undeclared section as user A and then as user B.
## Expected behavior
Two requests that differ in a declared dimension get different entries; two that do not, share one.
### Must hold
* For every dimension, two requests differing only in that dimension produce different `Guid`s, and two identical requests inside the entry's lifetime produce the same one.
* The `*` query section produces a new `Guid` when any query parameter changes, including one the page ignores; the named section does not.
* Requests differing only in the order of distinct query parameter names, with each name's values unchanged, share an entry. Changing a declared value produces a different entry.
* User B never sees content generated for user A in the user-varying section, including on the first request after A's, and an anonymous request is never served a signed-in user's cached section.
* For the unannotated cacheable child in the no-variation control, user B receives the content first cached for user A while that entry remains valid.
## Evidence to capture
* A table with one row per dimension: request A, request B, `Guid` A, `Guid` B, hit or miss.
* The full output and child-component configuration for the undeclared section as user A and then user B, plus any exception or log entry.
## Documentation to use
* [ASP.NET Core Blazor CacheView component](https://learn.microsoft.com/en-us/aspnet/core/blazor/state-management/cacheview-component?view=aspnetcore-11.0)
Report whether the docs warn about caching user-specific content and say what happens when a section forgets to declare a dimension.
## What to report
Report results using the format described in the [validation testing manual](https://github.com/dotnet/aspnetcore/issues/68479). Include link to a repository with the test app.
Contributor guide
Assessment
This issue has not been assessed yet.