diff --git a/src/Components/Components/src/PersistentState/PersistentServicesRegistry.cs b/src/Components/Components/src/PersistentState/PersistentServicesRegistry.cs index 3a91043bb6d9..aa7d537faf57 100644 --- a/src/Components/Components/src/PersistentState/PersistentServicesRegistry.cs +++ b/src/Components/Components/src/PersistentState/PersistentServicesRegistry.cs @@ -206,13 +206,28 @@ public PropertiesAccessor([DynamicallyAccessedMembers(LinkerFlags.Component)] Ty public (string, Type)[] KeyTypePairs => _cachedKeysForService; + // Mapping for internal classes bundled in different assemblies during prerendering and WASM rendering. + private static readonly Dictionary _canonicalMap = new(StringComparer.OrdinalIgnoreCase) + { + { "Microsoft.AspNetCore.Components.Endpoints", "Microsoft.AspNetCore.Components" }, + { "Microsoft.AspNetCore.Components.WebAssembly", "Microsoft.AspNetCore.Components" } + }; + 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 assemblySimpleName = keyType.Assembly.GetName().Name ?? ""; + if (_canonicalMap.TryGetValue(assemblySimpleName, out var canonicalAssembly)) + { + assemblyName = canonicalAssembly; + } + var typeName = keyType.FullName; - var input = Encoding.UTF8.GetBytes(string.Join(".", assemblyName, typeName, propertyName)); - return Convert.ToBase64String(SHA256.HashData(input)); + var inputString = 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/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));