SupplyParameterFromTempData support for Blazor#65306
SupplyParameterFromTempData support for Blazor#65306dariatiurina wants to merge 68 commits intodotnet:mainfrom
Conversation
src/Components/Components/src/SupplyParameterFromTempDataServiceCollectionExtensions.cs
Show resolved
Hide resolved
src/Components/Components/src/SupplyParameterFromTempDataValueProvider.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Does setting the property causes it to be saved to temp data for the next navigation?
There was a problem hiding this comment.
Yes. But reading through this property always goes through Get() so it only persists once.
There was a problem hiding this comment.
Pull request overview
This pull request adds a new [SupplyParameterFromTempData] attribute for Blazor server-side rendering components, enabling automatic read/write of TempData values. This follows the established pattern of [SupplyParameterFromQuery] and [SupplyParameterFromForm] attributes, providing a more convenient alternative to manually accessing TempData via cascading parameters. The implementation includes a value mapper service that reads TempData values on component initialization and persists property values back to TempData before the response is sent.
Changes:
- New
SupplyParameterFromTempDataAttributein Components assembly for marking properties ITempDataValueMapperinterface andTempDataValueMapperimplementation for managing read/write operationsSupplyParameterFromTempDataValueProvideras the cascading value supplier- Automatic registration in
AddRazorComponents()with service collection extensions - Integration hooks in
EndpointHtmlRendererandTempDataServicefor initialization and persistence - Unit and E2E tests covering basic scenarios
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Components/Components/src/SupplyParameterFromTempDataAttribute.cs | New attribute definition inheriting from CascadingParameterAttributeBase |
| src/Components/Endpoints/src/TempData/ITempDataValueMapper.cs | Public interface for TempData value mapping operations |
| src/Components/Endpoints/src/TempData/TempDataValueMapper.cs | Core implementation handling read/write with callback registration |
| src/Components/Endpoints/src/TempData/SupplyParameterFromTempDataValueProvider.cs | ICascadingValueSupplier implementation providing TempData values to components |
| src/Components/Endpoints/src/TempData/SupplyParameterFromTempDataServiceCollectionExtensions.cs | Service registration extension method |
| src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.cs | Adds initialization hook for TempDataValueMapper |
| src/Components/Endpoints/src/DependencyInjection/TempDataService.cs | Adds persistence hook to invoke registered callbacks |
| src/Components/Endpoints/src/DependencyInjection/RazorComponentsServiceCollectionExtensions.cs | Registers TempDataValueMapper and provider services automatically |
| src/Components/Endpoints/test/TempData/TempDataValueMapperTest.cs | Comprehensive unit tests for the mapper implementation |
| src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/TempData/TempDataComponent.razor | Test component demonstrating usage |
| src/Components/test/E2ETest/Tests/TempDataCookieTest.cs | E2E test for cookie-based TempData provider |
| src/Components/test/E2ETest/Tests/TempDataSessionStorageTest.cs | E2E test for session storage TempData provider |
| src/Components/Endpoints/src/PublicAPI.Unshipped.txt | API surface additions |
| src/Components/Components/src/PublicAPI.Unshipped.txt | API surface additions for attribute |
| src/Components/Components/src/Microsoft.AspNetCore.Components.csproj | Adds InternalsVisibleTo for Endpoints assembly |
| src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.Prerendering.cs | Visibility modifier updates |
| src/Components/Endpoints/src/Rendering/EndpointComponentState.cs | Visibility modifier updates |
| src/Framework/App.Runtime/src/CompatibilitySuppressions.xml | BOM character removal |
src/Components/Components/src/SupplyParameterFromTempDataAttribute.cs
Outdated
Show resolved
Hide resolved
src/Components/Components/src/SupplyParameterFromTempDataValueProvider.cs
Show resolved
Hide resolved
src/Components/test/E2ETest/Tests/TempDataSessionStorageTest.cs
Outdated
Show resolved
Hide resolved
src/Components/Endpoints/src/TempData/SupplyParameterFromTempDataValueProvider.cs
Outdated
Show resolved
Hide resolved
src/Components/test/E2ETest/Tests/TempDataSessionStorageTest.cs
Outdated
Show resolved
Hide resolved
src/Components/Endpoints/src/TempData/SupplyParameterFromTempDataValueProvider.cs
Outdated
Show resolved
Hide resolved
src/Components/Components/src/SupplyParameterFromTempDataServiceCollectionExtensions.cs
Show resolved
Hide resolved
...test/testassets/Components.TestServer/RazorComponents/Pages/TempData/TempDataComponent.razor
Outdated
Show resolved
Hide resolved
…bute.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…onents/Pages/TempData/TempDataComponent.razor Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
src/Components/Endpoints/src/Rendering/EndpointComponentState.cs
Outdated
Show resolved
Hide resolved
|
|
||
| namespace Microsoft.AspNetCore.Components.Endpoints; | ||
|
|
||
| internal partial class TempDataValueMapper : ITempDataValueMapper |
There was a problem hiding this comment.
Can you name a scenario where someone would want to implement a custom ITempDataValueMapper or
use ITempDataValueMapper directly without SupplyParameterFromTempDataValueProvider? I cannot think of any. I think we can simplify the implementation.
TempDataValueMapper and ITempDataValueMapper have 3 unrelated responsibilities: getting value, managing callbacks and being DI injection point. We could merge it into SupplyParameterFromTempDataValueProvider that would have the responsibility of supplying the values to compoennts that declare [SupplyParameterFromTempData], reading, subscribing etc would be just implementation details of that responsibility.
That would shrink the volume of new public APIs and would make it easier to read the code. It would require removing TempDataValueMapper and ITempDataValueMapper, moving PersistValues and GetTempData to SupplyParameterFromTempDataValueProvider together with subscription bits. + updating the registration to services.TryAddScoped<SupplyParameterFromTempDataValueProvider>();
SupplyParameterFromTempData
Summary
Provides
[SupplyParameterFromTempData]attribute for Blazor SSR components to read and write TempData values, consistent with[SupplyParameterFromQuery]and[SupplyParameterFromForm]patterns.Motivation
While TempData is accessible via the
[CascadingParameter] ITempDataapproach, many scenarios only need simple read/write of a single value. The attribute-based approach:SupplyParameterFrom*attributesTempData["key"]accessDesign
Attribute
ITempDataValueMapper Interface
The mapper interface bridges the value provider and the underlying TempData store:
GetValuereads from TempData usingGet()semantics (marks for deletion).RegisterValueCallbackstores a getter that will be invoked at persist time to capture the current property value.DeleteValueCallbackremoves a registered callback (called on component unsubscribe to prevent memory leaks).Lifecycle
TempDataValueMapper.SetRequestContext(HttpContext)is called duringEndpointHtmlRenderer.InitializeStandardComponentServicesAsync, wiring the mapper to the current request.SupplyParameterFromTempDataValueProvider.Subscribe()uses reflection to create aPropertyGetterfor the decorated property and registers a callback viaITempDataValueMapper.RegisterValueCallback().SupplyParameterFromTempDataValueProvider.GetCurrentValue()reads the value from TempData usingITempDataValueMapper.GetValue()(which callsTempData.Get()— marks for deletion). Deserialization errors are caught and logged, returningnull.TempDataService.Save()is called, it invokesTempDataValueMapper.PersistValues(tempData), which iterates all registered callbacks, reads the current property values from the components, and writes them back into TempData. Callback exceptions are caught, logged, and do not prevent other keys from being persisted.SupplyParameterFromTempDataValueProvider.Unsubscribe()callsITempDataValueMapper.DeleteValueCallback()to remove the registered callback.Implementation Details
StringComparer.OrdinalIgnoreCase.RegisterValueCallbackthrowsInvalidOperationExceptionif a callback is already registered for the same key — multiple components cannot bind to the same TempData key.TempDataValueMapperreads TempData fromHttpContext.Items[typeof(ITempData)], the same single instance used by the cascading parameter approach.TempDataPersistFail(callback exception during persist) andTempDataDeserializeFail(JsonExceptionduring read).Registration
Automatically enabled when calling
AddRazorComponents():AddSupplyValueFromTempDataProviderregisters theSupplyParameterFromTempDataValueProvideras a scopedICascadingValueSupplier:Usage
Basic:
Custom key:
Form with redirect:
Out of Scope
Peek()andKeep()semantics - use[CascadingParameter] ITempDatafor advanced controlRisks
TempData["key"]directly and[SupplyParameterFromTempData]for the same key, the final persisted value depends on execution order. Mitigation: Document that mixing approaches for the same key is unsupported.[SupplyParameterFromTempData]with the same key, the secondRegisterValueCallbackcall throwsInvalidOperationException. Mitigation: Error message clearly identifies the conflicting key.Fixes #49683