diff --git a/src/Components/Components/src/PersistentState/PersistentServicesRegistry.cs b/src/Components/Components/src/PersistentState/PersistentServicesRegistry.cs index 3a91043bb6d9..64936326e3e5 100644 --- a/src/Components/Components/src/PersistentState/PersistentServicesRegistry.cs +++ b/src/Components/Components/src/PersistentState/PersistentServicesRegistry.cs @@ -211,8 +211,16 @@ private static string ComputeKey(Type keyType, string propertyName) // This happens once per type and property combo, so allocations are ok. var assemblyName = keyType.Assembly.FullName; var typeName = keyType.FullName; - var input = Encoding.UTF8.GetBytes(string.Join(".", assemblyName, typeName, propertyName)); - return Convert.ToBase64String(SHA256.HashData(input)); + + // Internal classes can be bundled in different assemblies during prerendering and WASM rendering. + bool isTypeInternal = (!keyType.IsPublic && !keyType.IsNested) || keyType.IsNestedAssembly; + var inputString = isTypeInternal + ? string.Join(".", typeName, propertyName) + : string.Join(".", assemblyName, typeName, propertyName); + + var input = Encoding.UTF8.GetBytes(inputString); + var hash = SHA256.HashData(input); + return Convert.ToBase64String(hash); } internal static IEnumerable GetCandidateBindableProperties( diff --git a/src/Components/Endpoints/src/DependencyInjection/RazorComponentsServiceCollectionExtensions.cs b/src/Components/Endpoints/src/DependencyInjection/RazorComponentsServiceCollectionExtensions.cs index 302dec7dcb16..5ed3f2ce195d 100644 --- a/src/Components/Endpoints/src/DependencyInjection/RazorComponentsServiceCollectionExtensions.cs +++ b/src/Components/Endpoints/src/DependencyInjection/RazorComponentsServiceCollectionExtensions.cs @@ -75,6 +75,7 @@ public static IRazorComponentsBuilder AddRazorComponents(this IServiceCollection services.TryAddScoped(); services.TryAddScoped(); + RegisterPersistentComponentStateServiceCollectionExtensions.AddPersistentServiceRegistration(services, RenderMode.InteractiveWebAssembly); // Form handling services.AddSupplyValueFromFormProvider(); diff --git a/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.cs b/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.cs index e99574aa881e..d7e6a20d698c 100644 --- a/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.cs +++ b/src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.cs @@ -149,7 +149,7 @@ private static void InitializeResourceCollection(HttpContext httpContext) var resourceCollectionProvider = resourceCollectionUrl != null ? httpContext.RequestServices.GetService() : null; if (resourceCollectionUrl != null && resourceCollectionProvider != null) { - resourceCollectionProvider.SetResourceCollectionUrl(resourceCollectionUrl.Url); + resourceCollectionProvider.ResourceCollectionUrl = resourceCollectionUrl.Url; resourceCollectionProvider.SetResourceCollection(resourceCollection ?? ResourceAssetCollection.Empty); } } diff --git a/src/Components/Endpoints/test/RazorComponentsServiceCollectionExtensionsTest.cs b/src/Components/Endpoints/test/RazorComponentsServiceCollectionExtensionsTest.cs index 09dfd3a90403..40efccecb1bf 100644 --- a/src/Components/Endpoints/test/RazorComponentsServiceCollectionExtensionsTest.cs +++ b/src/Components/Endpoints/test/RazorComponentsServiceCollectionExtensionsTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Forms; using Microsoft.AspNetCore.Components.Forms.Mapping; using Microsoft.AspNetCore.Components.Routing; using Microsoft.AspNetCore.Hosting; @@ -92,7 +93,12 @@ private Dictionary MultiRegistrationServiceTypes { typeof(SupplyParameterFromFormValueProvider), typeof(SupplyParameterFromQueryValueProvider), - } + }, + [typeof(IPersistentServiceRegistration)] = new[] + { + typeof(ResourceCollectionProvider), + typeof(AntiforgeryStateProvider), + }, }; } } diff --git a/src/Components/Shared/src/ResourceCollectionProvider.cs b/src/Components/Shared/src/ResourceCollectionProvider.cs index 78ebef9d13d2..030d4b0e684c 100644 --- a/src/Components/Shared/src/ResourceCollectionProvider.cs +++ b/src/Components/Shared/src/ResourceCollectionProvider.cs @@ -11,36 +11,27 @@ namespace Microsoft.AspNetCore.Components; internal class ResourceCollectionProvider { - private const string ResourceCollectionUrlKey = "__ResourceCollectionUrl"; private string? _url; - private ResourceAssetCollection? _resourceCollection; - private readonly PersistentComponentState _state; - private readonly IJSRuntime _jsRuntime; - [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Strings are not trimmed")] - public ResourceCollectionProvider(PersistentComponentState state, IJSRuntime jsRuntime) + [SupplyParameterFromPersistentComponentState] + public string? ResourceCollectionUrl { - _state = state; - _jsRuntime = jsRuntime; - _ = _state.TryTakeFromJson(ResourceCollectionUrlKey, out _url); + get => _url; + set + { + if (_url != null) + { + throw new InvalidOperationException("The resource collection URL has already been set."); + } + _url = value; + } } - [MemberNotNull(nameof(_url))] - [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Strings are not trimmed")] - internal void SetResourceCollectionUrl(string url) + private ResourceAssetCollection? _resourceCollection; + private readonly IJSRuntime _jsRuntime; + public ResourceCollectionProvider(IJSRuntime jsRuntime) { - if (_url != null) - { - throw new InvalidOperationException("The resource collection URL has already been set."); - } - _url = url; - PersistingComponentStateSubscription registration = default; - registration = _state.RegisterOnPersisting(() => - { - _state.PersistAsJson(ResourceCollectionUrlKey, _url); - registration.Dispose(); - return Task.CompletedTask; - }, RenderMode.InteractiveWebAssembly); + _jsRuntime = jsRuntime; } internal async Task GetResourceCollection() diff --git a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs index 218936a9c1d8..e90fdab63e70 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs @@ -308,6 +308,7 @@ internal void InitializeDefaultServices() Services.AddSupplyValueFromPersistentComponentStateProvider(); Services.AddSingleton(); Services.AddSingleton(); + RegisterPersistentComponentStateServiceCollectionExtensions.AddPersistentServiceRegistration(Services, RenderMode.InteractiveWebAssembly); Services.AddLogging(builder => { builder.AddProvider(new WebAssemblyConsoleLoggerProvider(DefaultWebAssemblyJSRuntime.Instance));