diff --git a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs index 56b251f19db7a0e3f15fa6482e894c87d001bdb1..f38ba877a1e73d63b59bf9e87d39ac35eb28b58a 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs @@ -1,10 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Reflection.Metadata; -using System.Text.Json; using Microsoft.AspNetCore.Components.Lifetime; -using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web.Infrastructure; using Microsoft.AspNetCore.Components.WebAssembly.HotReload; using Microsoft.AspNetCore.Components.WebAssembly.Infrastructure; @@ -12,7 +11,6 @@ using Microsoft.AspNetCore.Components.WebAssembly.Rendering; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.JSInterop; namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting { @@ -27,7 +25,6 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting private readonly IConfiguration _configuration; private readonly RootComponentMappingCollection _rootComponents; private readonly string? _persistedState; - private readonly JsonSerializerOptions _jsonOptions; // NOTE: the host is disposable because it OWNs references to disposable things. // @@ -46,8 +43,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting WebAssemblyHostBuilder builder, IServiceProvider services, AsyncServiceScope scope, - string? persistedState, - JsonSerializerOptions jsonOptions) + string? persistedState) { // To ensure JS-invoked methods don't get linked out, have a reference to their enclosing types GC.KeepAlive(typeof(JSInteropMethods)); @@ -57,7 +53,6 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting _configuration = builder.Configuration; _rootComponents = builder.RootComponents; _persistedState = persistedState; - _jsonOptions = jsonOptions; } /// <summary> diff --git a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs index 921e317810edc7ae4e574df9dedc84681a364cd9..9f0a7a7aa7c552c81f6c618e68710fe2faf6ebc4 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs @@ -43,7 +43,6 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting { // We don't use the args for anything right now, but we want to accept them // here so that it shows up this way in the project templates. - args ??= Array.Empty<string>(); var jsRuntime = DefaultWebAssemblyJSRuntime.Instance; var builder = new WebAssemblyHostBuilder( jsRuntime, @@ -241,7 +240,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting var services = _createServiceProvider(); var scope = services.GetRequiredService<IServiceScopeFactory>().CreateAsyncScope(); - return new WebAssemblyHost(this, services, scope, _persistedState, _jsonOptions); + return new WebAssemblyHost(this, services, scope, _persistedState); } internal void InitializeDefaultServices() diff --git a/src/Components/WebAssembly/WebAssembly/src/HotReload/HotReloadAgent.cs b/src/Components/WebAssembly/WebAssembly/src/HotReload/HotReloadAgent.cs index f67f66cd19fce6398c759c1937977dd6d65c5888..db2817a9542341df422e5ca5d090b9a642938593 100644 --- a/src/Components/WebAssembly/WebAssembly/src/HotReload/HotReloadAgent.cs +++ b/src/Components/WebAssembly/WebAssembly/src/HotReload/HotReloadAgent.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// Based on the implementation in https://raw.githubusercontent.com/dotnet/sdk/4eaeb44f850903af2e0da8d74b7796f9df11c29d/src/BuiltInTools/DotNetDeltaApplier/HotReloadAgent.cs +// Based on the implementation in https://raw.githubusercontent.com/dotnet/sdk/4a0473b29bfd4c7bdda6080d821788ee6d50c86b/src/BuiltInTools/DotNetDeltaApplier/HotReloadAgent.cs using System; using System.Collections.Concurrent; @@ -187,8 +187,6 @@ namespace Microsoft.Extensions.HotReload _handlerActions ??= GetMetadataUpdateHandlerActions(); var handlerActions = _handlerActions; - // TODO: Get types to pass in - Type[]? updatedTypes = null; for (var i = 0; i < deltas.Count; i++) { @@ -206,6 +204,8 @@ namespace Microsoft.Extensions.HotReload cachedDeltas.Add(item); } + Type[]? updatedTypes = GetMetadataUpdateTypes(deltas); + handlerActions.ClearCache.ForEach(a => a(updatedTypes)); handlerActions.UpdateApplication.ForEach(a => a(updatedTypes)); @@ -217,6 +217,34 @@ namespace Microsoft.Extensions.HotReload } } + private Type[] GetMetadataUpdateTypes(IReadOnlyList<UpdateDelta> deltas) + { + List<Type>? types = null; + + foreach (var delta in deltas) + { + var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(assembly => TryGetModuleId(assembly) is Guid moduleId && moduleId == delta.ModuleId); + if (assembly is null) + { + continue; + } + + var assemblyTypes = assembly.GetTypes(); + + foreach (var updatedType in delta.UpdatedTypes ?? Array.Empty<int>()) + { + var type = assemblyTypes.FirstOrDefault(t => t.MetadataToken == updatedType); + if (type != null) + { + types ??= new(); + types.Add(type); + } + } + } + + return types?.ToArray() ?? Type.EmptyTypes; + } + public void ApplyDeltas(Assembly assembly, IReadOnlyList<UpdateDelta> deltas) { try diff --git a/src/Components/WebAssembly/WebAssembly/src/HotReload/UpdateDelta.cs b/src/Components/WebAssembly/WebAssembly/src/HotReload/UpdateDelta.cs index 7c1d5c634279aad5c1d503889312d6ec2e8ad00a..8d0dd1fb56eb184328faecfb423fd2db0a411329 100644 --- a/src/Components/WebAssembly/WebAssembly/src/HotReload/UpdateDelta.cs +++ b/src/Components/WebAssembly/WebAssembly/src/HotReload/UpdateDelta.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; - namespace Microsoft.Extensions.HotReload { internal sealed class UpdateDelta @@ -12,5 +10,7 @@ namespace Microsoft.Extensions.HotReload public byte[] MetadataDelta { get; set; } = default!; public byte[] ILDelta { get; set; } = default!; + + public int[]? UpdatedTypes { get; set; } } } diff --git a/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs b/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs index 68088b5c6c27b30e19e47b431730d4fc758eba98..271ea4cf638cf8d705147d493b57e0c4e54ee98a 100644 --- a/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs +++ b/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs @@ -1,11 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.ComponentModel; using System.Diagnostics; using System.Reflection; -using System.Threading.Tasks; using Microsoft.AspNetCore.Components.WebAssembly.Services; using Microsoft.Extensions.HotReload; using Microsoft.JSInterop; @@ -27,16 +25,10 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.HotReload internal static async Task InitializeAsync() { - // Determine if we're running under a hot reload environment (e.g. dotnet-watch). - // It's insufficient to know it the app can be hot reloaded (HotReloadFeature.IsSupported), - // since the hot-reload agent might be unavailable. - if (Environment.GetEnvironmentVariable("DOTNET_MODIFIABLE_ASSEMBLIES") != "debug") - { - return; - } - _hotReloadAgent = new HotReloadAgent(m => Debug.WriteLine(m)); + // Attempt to read previously applied hot reload deltas. dotnet-watch and VS will serve the script that can provide results from local-storage and + // the injected middleware if present. var jsObjectReference = (IJSUnmarshalledObjectReference)(await DefaultWebAssemblyJSRuntime.Instance.InvokeAsync<IJSObjectReference>("import", "./_framework/blazor-hotreload.js")); await jsObjectReference.InvokeUnmarshalled<Task<int>>("receiveHotReload"); }