| File: AppModels\HotReloadAppModel.cs | Web Access |
| Project: src\sdk\src\Dotnet.Watch\Watch\Microsoft.DotNet.HotReload.Watch.csproj (Microsoft.DotNet.HotReload.Watch) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Build.Graph; using Microsoft.DotNet.HotReload; using Microsoft.Extensions.Logging; namespace Microsoft.DotNet.Watch; internal abstract partial class HotReloadAppModel() { public abstract ValueTask<HotReloadClients> CreateClientsAsync(ILogger clientLogger, ILogger agentLogger, CancellationToken cancellationToken); protected static string GetInjectedAssemblyPath(string targetFramework, string assemblyName) => Path.Combine(Path.GetDirectoryName(typeof(HotReloadAppModel).Assembly.Location)!, "hotreload", targetFramework, assemblyName + ".dll"); public static string GetStartupHookPath(ProjectGraphNode project) { var hookTargetFramework = project.GetTargetFrameworkVersion() is { Major: >= 10 } ? "net10.0" : "net6.0"; return GetInjectedAssemblyPath(hookTargetFramework, "Microsoft.Extensions.DotNetDeltaApplier"); } public static HotReloadAppModel InferFromProject(DotNetWatchContext context, ProjectGraphNode projectNode) { var capabilities = projectNode.GetCapabilities(); if (capabilities.Contains(ProjectCapability.WebAssembly)) { context.Logger.Log(MessageDescriptor.ApplicationKind_BlazorWebAssembly); return new BlazorWebAssemblyAppModel(context, clientProject: projectNode); } if (capabilities.Contains(ProjectCapability.AspNetCore)) { if (projectNode.GetDescendantsAndSelf().FirstOrDefault(static p => p.GetCapabilities().Contains(ProjectCapability.WebAssembly)) is { } clientProject) { context.Logger.Log(MessageDescriptor.ApplicationKind_BlazorHosted, projectNode.ProjectInstance.FullPath, clientProject.ProjectInstance.FullPath); return new BlazorWebAssemblyHostedAppModel(context, clientProject: clientProject, serverProject: projectNode); } context.Logger.Log(MessageDescriptor.ApplicationKind_WebApplication); return new WebServerAppModel(context, serverProject: projectNode); } if (capabilities.Contains(ProjectCapability.HotReloadWebSockets)) { context.Logger.Log(MessageDescriptor.ApplicationKind_WebSockets); return new MobileAppModel(context, projectNode); } context.Logger.Log(MessageDescriptor.ApplicationKind_Default); return new DefaultAppModel(projectNode); } /// <summary> /// True if a managed code agent can be injected into the target process. /// The agent is injected either via dotnet startup hook, or via web server middleware for WASM clients. /// </summary> internal static bool IsManagedAgentSupported(ProjectGraphNode project, ILogger logger) { if (!project.IsNetCoreApp(Versions.Version6_0)) { logger.Log(MessageDescriptor.ProjectDoesNotSupportHotReload_TargetFramework, Versions.Version6_0); return false; } // Since .NET 11 the SDK generates runtimeconfig.dev.json file that configures the runtime to support // startup hooks and metadata update handlers in Debug builds. // The file is generated when the property EnableHotReloadInRuntimeConfigDevFile is true. if (project.IsNetCoreApp(Versions.Version11_0)) { if (!project.ProjectInstance.GetBooleanPropertyValue(PropertyNames.EnableHotReloadInRuntimeConfigDevFile, defaultValue: true)) { // If runtimeconfig.dev.json file is not generated MetadataUpdaterSupport and StartupHookSupport need to be enabled. var metadataUpdaterSupported = project.ProjectInstance.GetBooleanPropertyValue(PropertyNames.MetadataUpdaterSupport, defaultValue: true); if (!metadataUpdaterSupported || !StartupHookSupportedIfRequired()) { logger.Log( MessageDescriptor.ProjectDoesNotSupportHotReload_Property, // setting blocking Hot Reload: metadataUpdaterSupported ? PropertyNames.StartupHookSupport : PropertyNames.MetadataUpdaterSupport, "False", // recommended setting: PropertyNames.EnableHotReloadInRuntimeConfigDevFile, "True"); return false; } } } else { if (!StartupHookSupportedIfRequired()) { // Report which property is causing lack of support for startup hooks: var (propertyName, propertyValue) = project.ProjectInstance.GetBooleanPropertyValue(PropertyNames.PublishAot) ? (PropertyNames.PublishAot, true) : project.ProjectInstance.GetBooleanPropertyValue(PropertyNames.PublishTrimmed) ? (PropertyNames.PublishTrimmed, true) : (PropertyNames.StartupHookSupport, false); logger.Log(MessageDescriptor.ProjectDoesNotSupportHotReload_Property, propertyName, propertyValue.ToString(), PropertyNames.StartupHookSupport, "True"); return false; } // Not checking MetadataUpdateSupport since it's not set correctly prior .NET 11. // Use Optimize and DebugSymbols instead for older frameworks. // See https://github.com/dotnet/runtime/pull/127163 if (project.ProjectInstance.GetBooleanPropertyValue(PropertyNames.Optimize)) { logger.Log(MessageDescriptor.ProjectDoesNotSupportHotReload_Property, PropertyNames.Optimize, "True", PropertyNames.Optimize, "False"); return false; } if (!project.ProjectInstance.GetBooleanPropertyValue(PropertyNames.DebugSymbols)) { logger.Log(MessageDescriptor.ProjectDoesNotSupportHotReload_Property, PropertyNames.DebugSymbols, "False", PropertyNames.DebugSymbols, "True"); return false; } } return true; bool StartupHookSupportedIfRequired() => project.ProjectInstance.GetBooleanPropertyValue(PropertyNames.StartupHookSupport, defaultValue: true) || // Startup hooks are not used for WASM projects. project.GetCapabilities().Contains(ProjectCapability.WebAssembly); } }