Browse Source

Remove stateful prerendering

Fixes: #12245
Fixes: #12630

This change removes stateful pre-rendering from Server-Side Blazor. This
means that when you render a component during the initial HTTP request,
we we will no longer preserve the component instances and their
parameters. While this feature was useful, it cause serious scalability
concerns.

This means that it will now be required to register "entry-point"
components in startup similar to client-side Blazor.
Ryan Nowak 6 years ago
parent
commit
45f50905d5
54 changed files with 96 additions and 1867 deletions
  1. 0 1
      eng/ProjectReferences.props
  2. 0 1
      eng/SharedFramework.Local.props
  3. 5 1
      src/Analyzers/Analyzers/test/TestFiles/CompilationFeatureDetectorTest/StartupWithMapBlazorHub.cs
  4. 0 15
      src/Components/Components.sln
  5. 0 18
      src/Components/Server/ref/Microsoft.AspNetCore.Components.Server.netcoreapp3.0.cs
  6. 0 36
      src/Components/Server/src/Builder/ComponentEndpointRouteBuilderExtensions.cs
  7. 7 46
      src/Components/Server/src/Circuits/CircuitHost.cs
  8. 0 207
      src/Components/Server/src/Circuits/CircuitPrerenderer.cs
  9. 1 1
      src/Components/Server/src/Circuits/DefaultCircuitFactory.cs
  10. 1 4
      src/Components/Server/src/ComponentDescriptor.cs
  11. 6 2
      src/Components/Server/src/ComponentHub.cs
  12. 0 6
      src/Components/Server/src/DependencyInjection/ComponentServiceCollectionExtensions.cs
  13. 0 33
      src/Components/Server/src/Prerendering/ComponentPrerenderResult.cs
  14. 0 29
      src/Components/Server/src/Prerendering/ComponentPrerenderingContext.cs
  15. 0 20
      src/Components/Server/src/Prerendering/IComponentPrerenderer.cs
  16. 0 253
      src/Components/Server/test/Circuits/CircuitPrerendererTest.cs
  17. 1 2
      src/Components/Server/test/Circuits/TestCircuitHost.cs
  18. 15 2
      src/Components/Server/test/ComponentEndpointRouteBuilderExtensionsTest.cs
  19. 0 0
      src/Components/Web.JS/dist/Release/blazor.server.js
  20. 0 0
      src/Components/Web.JS/dist/Release/blazor.webassembly.js
  21. 4 30
      src/Components/Web.JS/src/Boot.Server.ts
  22. 4 112
      src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts
  23. 0 46
      src/Components/Web.JS/src/Platform/Circuits/ComponentDescriptor.ts
  24. 0 146
      src/Components/Web.JS/tests/CircuitManager.test.ts
  25. 5 2
      src/Components/test/E2ETest/ServerExecutionTests/ServerSideAppTest.cs
  26. 0 1
      src/Components/test/testassets/ComponentsApp.Server/ComponentsApp.Server.csproj
  27. 0 0
      src/Components/test/testassets/ComponentsApp.Server/Pages/_Host.cshtml
  28. 2 3
      src/Components/test/testassets/ComponentsApp.Server/Startup.cs
  29. 2 16
      src/Components/test/testassets/Ignitor/Program.cs
  30. 3 3
      src/Components/test/testassets/TestServer/Components.TestServer.csproj
  31. 1 1
      src/Components/test/testassets/TestServer/Pages/PrerenderedHost.cshtml
  32. 0 11
      src/Mvc/Mvc.Components.Prerendering/ref/Microsoft.AspNetCore.Mvc.Components.Prerendering.csproj
  33. 0 12
      src/Mvc/Mvc.Components.Prerendering/ref/Microsoft.AspNetCore.Mvc.Components.Prerendering.netcoreapp3.0.cs
  34. 0 25
      src/Mvc/Mvc.Components.Prerendering/src/HtmlContentPrerenderComponentResultAdapter.cs
  35. 0 75
      src/Mvc/Mvc.Components.Prerendering/src/HtmlHelperComponentPrerenderingExtensions.cs
  36. 0 18
      src/Mvc/Mvc.Components.Prerendering/src/Microsoft.AspNetCore.Mvc.Components.Prerendering.csproj
  37. 0 512
      src/Mvc/Mvc.Components.Prerendering/test/HtmlHelperComponentPrerenderingExtensionsTests.cs
  38. 0 11
      src/Mvc/Mvc.Components.Prerendering/test/Microsoft.AspNetCore.Mvc.Components.Prerendering.Test.csproj
  39. 6 6
      src/Mvc/Mvc.ViewFeatures/ref/Microsoft.AspNetCore.Mvc.ViewFeatures.netcoreapp3.0.cs
  40. 4 4
      src/Mvc/Mvc.ViewFeatures/src/HtmlHelperComponentExtensions.cs
  41. 8 9
      src/Mvc/Mvc.ViewFeatures/test/HtmlHelperComponentExtensionsTests.cs
  42. 0 6
      src/Mvc/Mvc.sln
  43. 1 3
      src/Mvc/MvcNoDeps.slnf
  44. 0 1
      src/Mvc/samples/MvcSandbox/MvcSandbox.csproj
  45. 0 1
      src/Mvc/shared/Mvc.Views.TestCommon/Microsoft.AspNetCore.Mvc.Views.TestCommon.csproj
  46. 0 1
      src/Mvc/test/Mvc.FunctionalTests/BasicTests.cs
  47. 6 77
      src/Mvc/test/Mvc.FunctionalTests/ComponentRenderingFunctionalTests.cs
  48. 0 1
      src/Mvc/test/WebSites/BasicWebSite/BasicWebSite.csproj
  49. 7 11
      src/Mvc/test/WebSites/BasicWebSite/Controllers/RazorComponentsController.cs
  50. 1 1
      src/Mvc/test/WebSites/BasicWebSite/RazorComponents/FetchData.razor
  51. 4 31
      src/Mvc/test/WebSites/BasicWebSite/Views/RazorComponents/Index.cshtml
  52. 1 11
      src/Mvc/test/WebSites/BasicWebSite/Views/RazorComponents/Navigation.cshtml
  53. 0 2
      src/ProjectTemplates/ProjectTemplates.sln
  54. 1 1
      src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Pages/_Host.cshtml

+ 0 - 1
eng/ProjectReferences.props

@@ -101,7 +101,6 @@
     <ProjectReferenceProvider Include="Microsoft.AspNetCore.Razor" ProjectPath="$(RepoRoot)src\Razor\Razor\src\Microsoft.AspNetCore.Razor.csproj" RefProjectPath="$(RepoRoot)src\Razor\Razor\ref\Microsoft.AspNetCore.Razor.csproj" />
     <ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Abstractions" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Abstractions\src\Microsoft.AspNetCore.Mvc.Abstractions.csproj" RefProjectPath="$(RepoRoot)src\Mvc\Mvc.Abstractions\ref\Microsoft.AspNetCore.Mvc.Abstractions.csproj" />
     <ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.ApiExplorer" ProjectPath="$(RepoRoot)src\Mvc\Mvc.ApiExplorer\src\Microsoft.AspNetCore.Mvc.ApiExplorer.csproj" RefProjectPath="$(RepoRoot)src\Mvc\Mvc.ApiExplorer\ref\Microsoft.AspNetCore.Mvc.ApiExplorer.csproj" />
-    <ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Components.Prerendering" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Components.Prerendering\src\Microsoft.AspNetCore.Mvc.Components.Prerendering.csproj" RefProjectPath="$(RepoRoot)src\Mvc\Mvc.Components.Prerendering\ref\Microsoft.AspNetCore.Mvc.Components.Prerendering.csproj" />
     <ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Core" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Core\src\Microsoft.AspNetCore.Mvc.Core.csproj" RefProjectPath="$(RepoRoot)src\Mvc\Mvc.Core\ref\Microsoft.AspNetCore.Mvc.Core.csproj" />
     <ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Cors" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Cors\src\Microsoft.AspNetCore.Mvc.Cors.csproj" RefProjectPath="$(RepoRoot)src\Mvc\Mvc.Cors\ref\Microsoft.AspNetCore.Mvc.Cors.csproj" />
     <ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.DataAnnotations" ProjectPath="$(RepoRoot)src\Mvc\Mvc.DataAnnotations\src\Microsoft.AspNetCore.Mvc.DataAnnotations.csproj" RefProjectPath="$(RepoRoot)src\Mvc\Mvc.DataAnnotations\ref\Microsoft.AspNetCore.Mvc.DataAnnotations.csproj" />

+ 0 - 1
eng/SharedFramework.Local.props

@@ -73,7 +73,6 @@
     <AspNetCoreAppReference Include="Microsoft.AspNetCore.Razor" />
     <AspNetCoreAppReference Include="Microsoft.AspNetCore.Mvc.Abstractions" />
     <AspNetCoreAppReference Include="Microsoft.AspNetCore.Mvc.ApiExplorer" />
-    <AspNetCoreAppReference Include="Microsoft.AspNetCore.Mvc.Components.Prerendering" />
     <AspNetCoreAppReference Include="Microsoft.AspNetCore.Mvc.Core" />
     <AspNetCoreAppReference Include="Microsoft.AspNetCore.Mvc.Cors" />
     <AspNetCoreAppReference Include="Microsoft.AspNetCore.Mvc.DataAnnotations" />

+ 5 - 1
src/Analyzers/Analyzers/test/TestFiles/CompilationFeatureDetectorTest/StartupWithMapBlazorHub.cs

@@ -13,8 +13,12 @@ namespace Microsoft.AspNetCore.Analyzers.TestFiles.CompilationFeatureDetectorTes
 
             app.UseEndpoints(endpoints =>
             {
-                endpoints.MapBlazorHub();
+                endpoints.MapBlazorHub<App>("app");
             });
         }
+
+        public class App : Microsoft.AspNetCore.Components.ComponentBase
+        {
+        }
     }
 }

+ 0 - 15
src/Components/Components.sln

@@ -208,8 +208,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 		..\..\.editorconfig = ..\..\.editorconfig
 	EndProjectSection
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Components.Prerendering", "..\Mvc\Mvc.Components.Prerendering\src\Microsoft.AspNetCore.Mvc.Components.Prerendering.csproj", "{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.Protocols.Json", "..\SignalR\common\Protocols.Json\src\Microsoft.AspNetCore.SignalR.Protocols.Json.csproj", "{ED210157-461B-45BB-9D86-B81A62792C30}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.Client", "..\SignalR\clients\csharp\Client\src\Microsoft.AspNetCore.SignalR.Client.csproj", "{DA137BD4-F7F1-4D53-855F-5EC40CEA36B0}"
@@ -1330,18 +1328,6 @@ Global
 		{9088E4E4-B855-457F-AE9E-D86709A5E1F4}.Release|x64.Build.0 = Debug|Any CPU
 		{9088E4E4-B855-457F-AE9E-D86709A5E1F4}.Release|x86.ActiveCfg = Debug|Any CPU
 		{9088E4E4-B855-457F-AE9E-D86709A5E1F4}.Release|x86.Build.0 = Debug|Any CPU
-		{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|x64.Build.0 = Debug|Any CPU
-		{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|x86.Build.0 = Debug|Any CPU
-		{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|Any CPU.Build.0 = Release|Any CPU
-		{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|x64.ActiveCfg = Release|Any CPU
-		{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|x64.Build.0 = Release|Any CPU
-		{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|x86.ActiveCfg = Release|Any CPU
-		{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|x86.Build.0 = Release|Any CPU
 		{ED210157-461B-45BB-9D86-B81A62792C30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{ED210157-461B-45BB-9D86-B81A62792C30}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{ED210157-461B-45BB-9D86-B81A62792C30}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -1548,7 +1534,6 @@ Global
 		{04262990-929C-42BF-85A9-21C25FA95617} = {2FC10057-7A0A-4E34-8302-879925BC0102}
 		{DC47C40A-FC38-44E4-94A4-ADE794E76309} = {2FC10057-7A0A-4E34-8302-879925BC0102}
 		{9088E4E4-B855-457F-AE9E-D86709A5E1F4} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF}
-		{3A4132B6-60DA-43A0-8E7B-4BF346F3247C} = {2FC10057-7A0A-4E34-8302-879925BC0102}
 		{ED210157-461B-45BB-9D86-B81A62792C30} = {2FC10057-7A0A-4E34-8302-879925BC0102}
 		{DA137BD4-F7F1-4D53-855F-5EC40CEA36B0} = {2FC10057-7A0A-4E34-8302-879925BC0102}
 		{0CDAB70B-71DC-43BE-ACB7-AD2EE3541FFB} = {2FC10057-7A0A-4E34-8302-879925BC0102}

+ 0 - 18
src/Components/Server/ref/Microsoft.AspNetCore.Components.Server.netcoreapp3.0.cs

@@ -14,8 +14,6 @@ namespace Microsoft.AspNetCore.Builder
     }
     public static partial class ComponentEndpointRouteBuilderExtensions
     {
-        public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints) { throw null; }
-        public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Action<Microsoft.AspNetCore.Http.Connections.HttpConnectionDispatcherOptions> configureOptions) { throw null; }
         public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Type type, string selector) { throw null; }
         public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Type type, string selector, System.Action<Microsoft.AspNetCore.Http.Connections.HttpConnectionDispatcherOptions> configureOptions) { throw null; }
         public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Type componentType, string selector, string path) { throw null; }
@@ -36,22 +34,6 @@ namespace Microsoft.AspNetCore.Components.Server
         public System.TimeSpan DisconnectedCircuitRetentionPeriod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
         public System.TimeSpan JSInteropDefaultCallTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
     }
-    public partial class ComponentPrerenderingContext
-    {
-        public ComponentPrerenderingContext() { }
-        public System.Type ComponentType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
-        public Microsoft.AspNetCore.Http.HttpContext Context { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
-        public Microsoft.AspNetCore.Components.ParameterView Parameters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
-    }
-    public sealed partial class ComponentPrerenderResult
-    {
-        internal ComponentPrerenderResult() { }
-        public void WriteTo(System.IO.TextWriter writer) { }
-    }
-    public partial interface IComponentPrerenderer
-    {
-        System.Threading.Tasks.Task<Microsoft.AspNetCore.Components.Server.ComponentPrerenderResult> PrerenderComponentAsync(Microsoft.AspNetCore.Components.Server.ComponentPrerenderingContext context);
-    }
 }
 namespace Microsoft.AspNetCore.Components.Server.Circuits
 {

+ 0 - 36
src/Components/Server/src/Builder/ComponentEndpointRouteBuilderExtensions.cs

@@ -15,42 +15,6 @@ namespace Microsoft.AspNetCore.Builder
     /// </summary>
     public static class ComponentEndpointRouteBuilderExtensions
     {
-        /// <summary>
-        /// Maps the Blazor <see cref="Hub" /> to the default path.
-        /// </summary>
-        /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>
-        /// <returns>The <see cref="ComponentEndpointConventionBuilder"/>.</returns>
-        public static ComponentEndpointConventionBuilder MapBlazorHub(this IEndpointRouteBuilder endpoints)
-        {
-            if (endpoints == null)
-            {
-                throw new ArgumentNullException(nameof(endpoints));
-            }
-
-            return endpoints.MapBlazorHub(configureOptions: _ => { });
-        }
-
-        /// <summary>
-        /// Maps the Blazor <see cref="Hub" /> to the default path.
-        /// </summary>
-        /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>
-        /// <param name="configureOptions">A callback to configure dispatcher options.</param>
-        /// <returns>The <see cref="ComponentEndpointConventionBuilder"/>.</returns>
-        public static ComponentEndpointConventionBuilder MapBlazorHub(this IEndpointRouteBuilder endpoints, Action<HttpConnectionDispatcherOptions> configureOptions)
-        {
-            if (endpoints == null)
-            {
-                throw new ArgumentNullException(nameof(endpoints));
-            }
-
-            if (configureOptions == null)
-            {
-                throw new ArgumentNullException(nameof(configureOptions));
-            }
-
-            return new ComponentEndpointConventionBuilder(endpoints.MapHub<ComponentHub>(ComponentHub.DefaultPath, configureOptions));
-        }
-
         /// <summary>
         ///Maps the Blazor <see cref="Hub" /> to the default path and associates
         /// the component <typeparamref name="TComponent"/> to this hub instance as the given DOM <paramref name="selector"/>.

+ 7 - 46
src/Components/Server/src/Circuits/CircuitHost.cs

@@ -7,8 +7,6 @@ using System.Security.Claims;
 using System.Text.Json;
 using System.Threading;
 using System.Threading.Tasks;
-using Microsoft.AspNetCore.Components.Rendering;
-using Microsoft.AspNetCore.Components.Routing;
 using Microsoft.AspNetCore.Components.Web;
 using Microsoft.AspNetCore.Components.Web.Rendering;
 using Microsoft.Extensions.DependencyInjection;
@@ -57,7 +55,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
             CircuitClientProxy client,
             RendererRegistry rendererRegistry,
             RemoteRenderer renderer,
-            IList<ComponentDescriptor> descriptors,
+            IReadOnlyList<ComponentDescriptor> descriptors,
             RemoteJSRuntime jsRuntime,
             CircuitHandler[] circuitHandlers,
             ILogger logger)
@@ -92,24 +90,10 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
 
         public RendererRegistry RendererRegistry { get; }
 
-        public IList<ComponentDescriptor> Descriptors { get; }
+        public IReadOnlyList<ComponentDescriptor> Descriptors { get; }
 
         public IServiceProvider Services { get; }
 
-        public Task<ComponentRenderedText> PrerenderComponentAsync(Type componentType, ParameterView parameters)
-        {
-            return Renderer.Dispatcher.InvokeAsync(async () =>
-            {
-                var result = await Renderer.RenderComponentAsync(componentType, parameters);
-
-                // When we prerender we start the circuit in a disconnected state. As such, we only call
-                // OnCircuitOpenenedAsync here and when the client reconnects we run OnConnectionUpAsync
-                await OnCircuitOpenedAsync(CancellationToken.None);
-
-                return result;
-            });
-        }
-
         public void SetCircuitUser(ClaimsPrincipal user)
         {
             var authenticationStateProvider = Services.GetService<AuthenticationStateProvider>() as IHostEnvironmentAuthenticationStateProvider;
@@ -120,26 +104,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
             }
         }
 
-        internal void InitializeCircuitAfterPrerender(UnhandledExceptionEventHandler unhandledException)
-        {
-            if (!_initialized)
-            {
-                _initialized = true;
-                UnhandledException += unhandledException;
-                var uriHelper = (RemoteUriHelper)Services.GetRequiredService<IUriHelper>();
-                if (!uriHelper.HasAttachedJSRuntime)
-                {
-                    uriHelper.AttachJsRuntime(JSRuntime);
-                }
-
-                var navigationInterception = (RemoteNavigationInterception)Services.GetRequiredService<INavigationInterception>();
-                if (!navigationInterception.HasAttachedJSRuntime)
-                {
-                    navigationInterception.AttachJSRuntime(JSRuntime);
-                }
-            }
-        }
-
         internal void SendPendingBatches()
         {
             // Dispatch any buffered renders we accumulated during a disconnect.
@@ -188,7 +152,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
                     return;
                 }
 
-
                 await Renderer.Dispatcher.InvokeAsync(() =>
                 {
                     SetCurrentCircuitHost(this);
@@ -233,13 +196,11 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
                     // That's because AddComponentAsync waits for quiescence, which can take
                     // arbitrarily long. In the meantime we might need to be receiving and
                     // processing incoming JSInterop calls or similar.
-                    for (var i = 0; i < Descriptors.Count; i++)
+                    var count = Descriptors.Count;
+                    for (var i = 0; i < count; i++)
                     {
-                        var (componentType, domElementSelector, prerendered) = Descriptors[i];
-                        if (!prerendered)
-                        {
-                            await Renderer.AddComponentAsync(componentType, domElementSelector);
-                        }
+                        var (componentType, domElementSelector) = Descriptors[i];
+                        await Renderer.AddComponentAsync(componentType, domElementSelector);
                     }
                 }
                 catch (Exception ex)
@@ -256,7 +217,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
             try
             {
                 AssertInitialized();
-                if(assemblyName == "Microsoft.AspNetCore.Components.Web" && methodIdentifier == "DispatchEvent")
+                if (assemblyName == "Microsoft.AspNetCore.Components.Web" && methodIdentifier == "DispatchEvent")
                 {
                     Log.DispatchEventTroughJSInterop(_logger);
                     return;

+ 0 - 207
src/Components/Server/src/Circuits/CircuitPrerenderer.cs

@@ -1,207 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Linq;
-using System.Runtime.ExceptionServices;
-using System.Text.Json;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Components.Rendering;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Http.Extensions;
-
-namespace Microsoft.AspNetCore.Components.Server.Circuits
-{
-    internal class CircuitPrerenderer : IComponentPrerenderer
-    {
-        private static object CircuitHostKey = new object();
-        private static object CancellationStatusKey = new object();
-        private static readonly JsonSerializerOptions _jsonSerializationOptions =
-            new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
-
-        private readonly CircuitFactory _circuitFactory;
-        private readonly CircuitRegistry _registry;
-
-        public CircuitPrerenderer(
-            CircuitFactory circuitFactory,
-            CircuitRegistry registry)
-        {
-            _circuitFactory = circuitFactory;
-            _registry = registry;
-        }
-
-        public async Task<ComponentPrerenderResult> PrerenderComponentAsync(ComponentPrerenderingContext prerenderingContext)
-        {
-            var context = prerenderingContext.Context;
-            var cancellationStatus = GetOrCreateCancellationStatus(context);
-            if (cancellationStatus.Canceled)
-            {
-                // Avoid creating a circuit host if other component earlier in the pipeline already triggered
-                // cancellation (e.g., by navigating or throwing). Instead render nothing.
-                return new ComponentPrerenderResult(Array.Empty<string>());
-            }
-            var circuitHost = GetOrCreateCircuitHost(context, cancellationStatus);
-            ComponentRenderedText renderResult;
-            try
-            {
-                renderResult = await circuitHost.PrerenderComponentAsync(
-                    prerenderingContext.ComponentType,
-                    prerenderingContext.Parameters);
-            }
-            catch (NavigationException navigationException)
-            {
-                // Cleanup the state as we won't need it any longer.
-                // Signal callbacks that we don't have to register the circuit.
-                await CleanupCircuitState(context, cancellationStatus, circuitHost);
-
-                // Navigation was attempted during prerendering.
-                if (prerenderingContext.Context.Response.HasStarted)
-                {
-                    // We can't perform a redirect as the server already started sending the response.
-                    // This is considered an application error as the developer should buffer the response until
-                    // all components have rendered.
-                    throw new InvalidOperationException("A navigation command was attempted during prerendering after the server already started sending the response. " +
-                        "Navigation commands can not be issued during server-side prerendering after the response from the server has started. Applications must buffer the" +
-                        "reponse and avoid using features like FlushAsync() before all components on the page have been rendered to prevent failed navigation commands.", navigationException);
-                }
-
-                context.Response.Redirect(navigationException.Location);
-                return new ComponentPrerenderResult(Array.Empty<string>());
-            }
-            catch
-            {
-                // If prerendering any component fails, cancel prerendering entirely and dispose the DI scope
-                await CleanupCircuitState(context, cancellationStatus, circuitHost);
-                throw;
-            }
-
-            circuitHost.Descriptors.Add(new ComponentDescriptor
-            {
-                ComponentType = prerenderingContext.ComponentType,
-                Prerendered = true
-            });
-
-            var record = JsonSerializer.Serialize(new PrerenderedComponentRecord(
-                    // We need to do this due to the fact that -- is not allowed within HTML comments and HTML doesn't encode '-'.
-                    // We will never have '..' sequences because we Base64UrlEncode the circuit id
-                    circuitHost.CircuitId.Replace("--", ".."),
-                    circuitHost.Renderer.Id,
-                    renderResult.ComponentId),
-                _jsonSerializationOptions);
-
-            var result = (new[] {
-                $"<!-- M.A.C.Component: {record} -->",
-            }).Concat(renderResult.Tokens).Concat(
-                new[] {
-                    $"<!-- M.A.C.Component: {renderResult.ComponentId} -->"
-                });
-
-            return new ComponentPrerenderResult(result);
-        }
-
-        private PrerenderingCancellationStatus GetOrCreateCancellationStatus(HttpContext context)
-        {
-            if (context.Items.TryGetValue(CancellationStatusKey, out var existingValue))
-            {
-                return (PrerenderingCancellationStatus)existingValue;
-            }
-            else
-            {
-                var cancellationStatus = new PrerenderingCancellationStatus();
-                context.Items[CancellationStatusKey] = cancellationStatus;
-                return cancellationStatus;
-            }
-        }
-
-        private static async Task CleanupCircuitState(HttpContext context, PrerenderingCancellationStatus cancellationStatus, CircuitHost circuitHost)
-        {
-            cancellationStatus.Canceled = true;
-            context.Items.Remove(CircuitHostKey);
-            await circuitHost.DisposeAsync();
-        }
-
-        private CircuitHost GetOrCreateCircuitHost(HttpContext context, PrerenderingCancellationStatus cancellationStatus)
-        {
-            if (context.Items.TryGetValue(CircuitHostKey, out var existingHost))
-            {
-                return (CircuitHost)existingHost;
-            }
-            else
-            {
-                var result = _circuitFactory.CreateCircuitHost(
-                    context,
-                    client: new CircuitClientProxy(), // This creates an "offline" client.
-                    GetFullUri(context.Request),
-                    GetFullBaseUri(context.Request),
-                    context.User);
-
-                result.UnhandledException += CircuitHost_UnhandledException;
-                context.Response.OnCompleted(() =>
-                {
-                    result.UnhandledException -= CircuitHost_UnhandledException;
-                    if (!cancellationStatus.Canceled)
-                    {
-                        _registry.RegisterDisconnectedCircuit(result);
-                    }
-
-                    return Task.CompletedTask;
-                });
-                context.Items.Add(CircuitHostKey, result);
-
-                return result;
-            }
-        }
-
-        private void CircuitHost_UnhandledException(object sender, UnhandledExceptionEventArgs e)
-        {
-            // Throw all exceptions encountered during pre-rendering so the default developer
-            // error page can respond.
-            ExceptionDispatchInfo.Capture((Exception)e.ExceptionObject).Throw();
-        }
-
-        private string GetFullUri(HttpRequest request)
-        {
-            return UriHelper.BuildAbsolute(
-                request.Scheme,
-                request.Host,
-                request.PathBase,
-                request.Path,
-                request.QueryString);
-        }
-
-        private string GetFullBaseUri(HttpRequest request)
-        {
-            var result = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase);
-
-            // PathBase may be "/" or "/some/thing", but to be a well-formed base URI
-            // it has to end with a trailing slash
-            if (!result.EndsWith('/'))
-            {
-                result += '/';
-            }
-
-            return result;
-        }
-
-        private readonly struct PrerenderedComponentRecord
-        {
-            public PrerenderedComponentRecord(string circuitId, int rendererId, int componentId)
-            {
-                CircuitId = circuitId;
-                RendererId = rendererId;
-                ComponentId = componentId;
-            }
-
-            public string CircuitId { get; }
-
-            public int RendererId { get; }
-
-            public int ComponentId { get; }
-        }
-
-        private class PrerenderingCancellationStatus
-        {
-            public bool Canceled { get; set; }
-        }
-    }
-}

+ 1 - 1
src/Components/Server/src/Circuits/DefaultCircuitFactory.cs

@@ -100,7 +100,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
             return circuitHost;
         }
 
-        internal static IList<ComponentDescriptor> ResolveComponentMetadata(HttpContext httpContext, CircuitClientProxy client)
+        internal static List<ComponentDescriptor> ResolveComponentMetadata(HttpContext httpContext, CircuitClientProxy client)
         {
             if (!client.Connected)
             {

+ 1 - 4
src/Components/Server/src/DependencyInjection/ComponentDescriptor.cs → src/Components/Server/src/ComponentDescriptor.cs

@@ -11,13 +11,10 @@ namespace Microsoft.AspNetCore.Components.Server
 
         public string Selector { get; set; }
 
-        public bool Prerendered { get; set; }
-
-        public void Deconstruct(out Type componentType, out string selector, out bool prerendered)
+        public void Deconstruct(out Type componentType, out string selector)
         {
             componentType = ComponentType;
             selector = Selector;
-            prerendered = Prerendered;
         }
     }
 }

+ 6 - 2
src/Components/Server/src/ComponentHub.cs

@@ -48,6 +48,9 @@ namespace Microsoft.AspNetCore.Components.Server
         /// <summary>
         /// For unit testing only.
         /// </summary>
+        // We store the circuit host in Context.Items which is tied to the lifetime of the underlying
+        // SignalR connection. There's no need to clean this up, it's a non-owning reference and it
+        // will go away when the connection does.
         internal CircuitHost CircuitHost
         {
             get => (CircuitHost)Context.Items[CircuitKey];
@@ -65,7 +68,6 @@ namespace Microsoft.AspNetCore.Components.Server
                 return Task.CompletedTask;
             }
 
-            CircuitHost = null;
             if (exception != null)
             {
                 return _circuitRegistry.DisconnectAsync(circuitHost, Context.ConnectionId);
@@ -92,6 +94,8 @@ namespace Microsoft.AspNetCore.Components.Server
             {
                 Log.UnhandledExceptionInCircuit(_logger, circuitHost.CircuitId, e);
             }
+
+            await _circuitRegistry.DisconnectAsync(circuitHost, Context.ConnectionId);
         }
 
         /// <summary>
@@ -149,8 +153,8 @@ namespace Microsoft.AspNetCore.Components.Server
             if (circuitHost != null)
             {
                 CircuitHost = circuitHost;
+                CircuitHost.UnhandledException += CircuitHost_UnhandledException;
 
-                circuitHost.InitializeCircuitAfterPrerender(CircuitHost_UnhandledException);
                 circuitHost.SetCircuitUser(Context.User);
                 circuitHost.SendPendingBatches();
                 return true;

+ 0 - 6
src/Components/Server/src/DependencyInjection/ComponentServiceCollectionExtensions.cs

@@ -63,12 +63,6 @@ namespace Microsoft.Extensions.DependencyInjection
 
             services.TryAddSingleton<CircuitRegistry>();
 
-            // We explicitly take over the prerendering and components services here.
-            // We can't have two separate component implementations coexisting at the
-            // same time, so when you register components (Circuits) it takes over
-            // all the abstractions.
-            services.AddScoped<IComponentPrerenderer, CircuitPrerenderer>();
-
             // Standard razor component services implementations
             //
             // These intentionally replace the non-interactive versions included in MVC.

+ 0 - 33
src/Components/Server/src/Prerendering/ComponentPrerenderResult.cs

@@ -1,33 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Collections.Generic;
-using System.IO;
-
-namespace Microsoft.AspNetCore.Components.Server
-{
-    /// <summary>
-    /// Represents the result of a prerendering an <see cref="IComponent"/>.
-    /// </summary>
-    public sealed class ComponentPrerenderResult
-    {
-        private readonly IEnumerable<string> _result;
-
-        internal ComponentPrerenderResult(IEnumerable<string> result)
-        {
-            _result = result;
-        }
-
-        /// <summary>
-        /// Writes the prerendering result to the given <paramref name="writer"/>.
-        /// </summary>
-        /// <param name="writer">The <see cref="TextWriter"/> the results will be written to.</param>
-        public void WriteTo(TextWriter writer)
-        {
-            foreach (var element in _result)
-            {
-                writer.Write(element);
-            }
-        }
-    }
-}

+ 0 - 29
src/Components/Server/src/Prerendering/ComponentPrerenderingContext.cs

@@ -1,29 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using Microsoft.AspNetCore.Http;
-
-namespace Microsoft.AspNetCore.Components.Server
-{
-    /// <summary>
-    /// The context for prerendering a component.
-    /// </summary>
-    public class ComponentPrerenderingContext
-    {
-        /// <summary>
-        /// Gets or sets the component type.
-        /// </summary>
-        public Type ComponentType { get; set; }
-
-        /// <summary>
-        /// Gets or sets the parameters for the component.
-        /// </summary>
-        public ParameterView Parameters { get; set; }
-
-        /// <summary>
-        /// Gets or sets the <see cref="HttpContext"/> in which the prerendering has been initiated.
-        /// </summary>
-        public HttpContext Context { get; set; }
-    }
-}

+ 0 - 20
src/Components/Server/src/Prerendering/IComponentPrerenderer.cs

@@ -1,20 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Threading.Tasks;
-
-namespace Microsoft.AspNetCore.Components.Server
-{
-    /// <summary>
-    /// Prerrenders <see cref="IComponent"/> instances.
-    /// </summary>
-    public interface IComponentPrerenderer
-    {
-        /// <summary>
-        /// Prerrenders the component <see cref="ComponentPrerenderingContext.ComponentType"/>.
-        /// </summary>
-        /// <param name="context">The context in which the prerrendering is happening.</param>
-        /// <returns><see cref="Task{TResult}"/> that will complete when the prerendering is done.</returns>
-        Task<ComponentPrerenderResult> PrerenderComponentAsync(ComponentPrerenderingContext context);
-    }
-}

+ 0 - 253
src/Components/Server/test/Circuits/CircuitPrerendererTest.cs

@@ -1,253 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.IO;
-using System.Security.Claims;
-using System.Text.Json;
-using System.Text.RegularExpressions;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Components.Server.Circuits;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Logging.Abstractions;
-using Microsoft.Extensions.Options;
-using Moq;
-using Xunit;
-
-namespace Microsoft.AspNetCore.Components.Server.Tests.Circuits
-{
-    public class CircuitPrerendererTest
-    {
-        private static readonly Regex ContentWrapperRegex = new Regex(
-            "<!-- M.A.C.Component: {\"circuitId\":\"[^\"]+\",\"rendererId\":0,\"componentId\":0} -->(?<content>.*)<!-- M.A.C.Component: 0 -->",
-            RegexOptions.Compiled | RegexOptions.Singleline, TimeSpan.FromSeconds(1)); // Treat the entire input string as a single line
-
-        private static readonly Regex CircuitInfoRegex = new Regex(
-            "<!-- M.A.C.Component: (?<info>.*?) -->.*",
-            RegexOptions.Compiled | RegexOptions.Singleline, TimeSpan.FromSeconds(1)); // Treat the entire input string as a single line
-
-        // Because CircuitPrerenderer is a point of integration with HttpContext,
-        // it's not a good candidate for unit testing. The majority of prerendering
-        // unit tests should be elsewhere in HtmlRendererTests inside the
-        // Microsoft.AspNetCore.Components.Tests projects.
-        //
-        // The only unit tests added here should specifically be about how we're
-        // interacting with the HttpContext for configuring the prerenderer.
-
-        [Fact]
-        public async Task ExtractsUriFromHttpContext_EmptyPathBase()
-        {
-            // Arrange
-            var circuitFactory = new TestCircuitFactory();
-            var circuitRegistry = new CircuitRegistry(
-                Options.Create(new CircuitOptions()),
-                Mock.Of<ILogger<CircuitRegistry>>(),
-                TestCircuitIdFactory.CreateTestFactory());
-            var circuitPrerenderer = new CircuitPrerenderer(circuitFactory, circuitRegistry);
-            var httpContext = new DefaultHttpContext();
-            var httpRequest = httpContext.Request;
-            httpRequest.Scheme = "https";
-            httpRequest.Host = new HostString("example.com", 1234);
-            httpRequest.Path = "/some/path";
-
-            var prerenderingContext = new ComponentPrerenderingContext
-            {
-                ComponentType = typeof(UriDisplayComponent),
-                Parameters = ParameterView.Empty,
-                Context = httpContext
-            };
-
-            // Act
-            var result = await circuitPrerenderer.PrerenderComponentAsync(prerenderingContext);
-            // Assert
-            Assert.Equal(string.Join("", new[]
-            {
-                "The current URI is ",
-                "https://example.com:1234/some/path",
-                " within base URI ",
-                "https://example.com:1234/"
-            }), GetUnwrappedContent(result));
-        }
-
-        private string GetUnwrappedContent(ComponentPrerenderResult rawResult)
-        {
-            var writer = new StringWriter();
-            rawResult.WriteTo(writer);
-            return ContentWrapperRegex.Match(writer.ToString())
-                .Groups["content"].Value
-                .Replace("\r\n","\n");
-        }
-
-        private JsonDocument GetUnwrappedCircuitInfo(ComponentPrerenderResult rawResult)
-        {
-            var writer = new StringWriter();
-            rawResult.WriteTo(writer);
-            var circuitInfo = CircuitInfoRegex.Match(writer.ToString()).Groups["info"].Value;
-
-            return JsonDocument.Parse(circuitInfo);
-        }
-
-        [Fact]
-        public async Task ExtractsUriFromHttpContext_NonemptyPathBase()
-        {
-            // Arrange
-            var circuitFactory = new TestCircuitFactory();
-            var circuitRegistry = new CircuitRegistry(
-                Options.Create(new CircuitOptions()),
-                Mock.Of<ILogger<CircuitRegistry>>(),
-                TestCircuitIdFactory.CreateTestFactory());
-            var circuitPrerenderer = new CircuitPrerenderer(circuitFactory, circuitRegistry);
-            var httpContext = new DefaultHttpContext();
-            var httpRequest = httpContext.Request;
-            httpRequest.Scheme = "https";
-            httpRequest.Host = new HostString("example.com", 1234);
-            httpRequest.PathBase = "/my/dir";
-            httpRequest.Path = "/some/path";
-
-            var prerenderingContext = new ComponentPrerenderingContext
-            {
-                ComponentType = typeof(UriDisplayComponent),
-                Parameters = ParameterView.Empty,
-                Context = httpContext
-            };
-
-            // Act
-            var result = await circuitPrerenderer.PrerenderComponentAsync(prerenderingContext);
-
-            // Assert
-            Assert.Equal(string.Join("", new[]
-            {
-                "The current URI is ",
-                "https://example.com:1234/my/dir/some/path",
-                " within base URI ",
-                "https://example.com:1234/my/dir/"
-            }), GetUnwrappedContent(result));
-        }
-
-        [Fact]
-        public async Task ReplacesDashesWithDots_WhenTheyAppearInPairs()
-        {
-            // Arrange
-            var circuitFactory = new TestCircuitFactory(() => "--1234--");
-            var circuitRegistry = new CircuitRegistry(
-                Options.Create(new CircuitOptions()),
-                Mock.Of<ILogger<CircuitRegistry>>(),
-                TestCircuitIdFactory.CreateTestFactory());
-            var circuitPrerenderer = new CircuitPrerenderer(circuitFactory, circuitRegistry);
-            var httpContext = new DefaultHttpContext();
-            var httpRequest = httpContext.Request;
-            httpRequest.Scheme = "https";
-            httpRequest.Host = new HostString("example.com", 1234);
-            httpRequest.Path = "/some/path";
-
-            var prerenderingContext = new ComponentPrerenderingContext
-            {
-                ComponentType = typeof(UriDisplayComponent),
-                Parameters = ParameterView.Empty,
-                Context = httpContext
-            };
-
-            // Act
-            var result = await circuitPrerenderer.PrerenderComponentAsync(prerenderingContext);
-
-            // Assert
-            Assert.Equal("..1234..", GetUnwrappedCircuitInfo(result).RootElement.GetProperty("circuitId").GetString());
-        }
-
-        [Fact]
-        public async Task DisposesCircuitScopeEvenIfPrerenderingThrows()
-        {
-            // Arrange
-            var circuitFactory = new MockServiceScopeCircuitFactory();
-            var circuitRegistry = new CircuitRegistry(
-                Options.Create(new CircuitOptions()),
-                Mock.Of<ILogger<CircuitRegistry>>(),
-                TestCircuitIdFactory.CreateTestFactory());
-            var httpContext = new DefaultHttpContext();
-            var prerenderer = new CircuitPrerenderer(circuitFactory, circuitRegistry);
-            var prerenderingContext = new ComponentPrerenderingContext
-            {
-                ComponentType = typeof(ThrowExceptionComponent),
-                Parameters = ParameterView.Empty,
-                Context = httpContext
-            };
-
-            // Act
-            await Assert.ThrowsAsync<InvalidTimeZoneException>(async () =>
-                await prerenderer.PrerenderComponentAsync(prerenderingContext));
-
-            // Assert
-            circuitFactory.MockServiceScope.Verify(scope => scope.Dispose(), Times.Once());
-        }
-
-        class TestCircuitFactory : CircuitFactory
-        {
-            private readonly Func<string> _circuitIdFactory;
-
-            public TestCircuitFactory(Func<string> circuitIdFactory = null)
-            {
-                _circuitIdFactory = circuitIdFactory ?? (() => Guid.NewGuid().ToString());
-            }
-
-            public override CircuitHost CreateCircuitHost(HttpContext httpContext, CircuitClientProxy client, string uriAbsolute, string baseUriAbsolute, ClaimsPrincipal user)
-            {
-                var serviceCollection = new ServiceCollection();
-                serviceCollection.AddScoped<IUriHelper>(_ =>
-                {
-                    var uriHelper = new RemoteUriHelper(NullLogger<RemoteUriHelper>.Instance);
-                    uriHelper.InitializeState(uriAbsolute, baseUriAbsolute);
-                    return uriHelper;
-                });
-                var serviceScope = serviceCollection.BuildServiceProvider().CreateScope();
-                return TestCircuitHost.Create(_circuitIdFactory(), serviceScope);
-            }
-        }
-
-        class MockServiceScopeCircuitFactory : CircuitFactory
-        {
-            public Mock<IServiceScope> MockServiceScope { get; }
-                = new Mock<IServiceScope>();
-
-            public override CircuitHost CreateCircuitHost(HttpContext httpContext, CircuitClientProxy client, string uriAbsolute, string baseUriAbsolute, ClaimsPrincipal user)
-            {
-                return TestCircuitHost.Create(Guid.NewGuid().ToString(), MockServiceScope.Object);
-            }
-        }
-
-        class UriDisplayComponent : IComponent
-        {
-            private RenderHandle _renderHandle;
-
-            [Inject] IUriHelper UriHelper { get; set; }
-
-            public void Attach(RenderHandle renderHandle)
-            {
-                _renderHandle = renderHandle;
-            }
-
-            public Task SetParametersAsync(ParameterView parameters)
-            {
-                _renderHandle.Render(builder =>
-                {
-                    builder.AddContent(0, "The current URI is ");
-                    builder.AddContent(1, UriHelper.GetAbsoluteUri());
-                    builder.AddContent(2, " within base URI ");
-                    builder.AddContent(3, UriHelper.GetBaseUri());
-                });
-
-                return Task.CompletedTask;
-            }
-        }
-
-        class ThrowExceptionComponent : IComponent
-        {
-            public void Attach(RenderHandle renderHandle)
-                => throw new InvalidTimeZoneException();
-
-            public Task SetParametersAsync(ParameterView parameters)
-                 => Task.CompletedTask;
-        }
-    }
-}

+ 1 - 2
src/Components/Server/test/Circuits/TestCircuitHost.cs

@@ -11,7 +11,6 @@ using Microsoft.AspNetCore.SignalR;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging.Abstractions;
-using Microsoft.Extensions.Logging.Testing;
 using Microsoft.Extensions.Options;
 using Moq;
 
@@ -19,7 +18,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
 {
     internal class TestCircuitHost : CircuitHost
     {
-        private TestCircuitHost(string circuitId, IServiceScope scope, CircuitClientProxy client, RendererRegistry rendererRegistry, RemoteRenderer renderer, IList<ComponentDescriptor> descriptors, RemoteJSRuntime jsRuntime, CircuitHandler[] circuitHandlers, ILogger logger)
+        private TestCircuitHost(string circuitId, IServiceScope scope, CircuitClientProxy client, RendererRegistry rendererRegistry, RemoteRenderer renderer, IReadOnlyList<ComponentDescriptor> descriptors, RemoteJSRuntime jsRuntime, CircuitHandler[] circuitHandlers, ILogger logger)
             : base(circuitId, scope, client, rendererRegistry, renderer, descriptors, jsRuntime, circuitHandlers, logger)
         {
         }

+ 15 - 2
src/Components/Server/test/ComponentEndpointRouteBuilderExtensionsTest.cs

@@ -2,8 +2,8 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System.Diagnostics;
+using System.Threading.Tasks;
 using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Hosting;
 using Moq;
@@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Components.Server.Tests
                 .UseRouting()
                 .UseEndpoints(endpoints =>
             {
-                endpoints.MapBlazorHub(dispatchOptions => called = true);
+                endpoints.MapBlazorHub<MyComponent>("app", dispatchOptions => called = true);
             }).Build();
 
             // Assert
@@ -68,5 +68,18 @@ namespace Microsoft.AspNetCore.Components.Server.Tests
 
             return new ApplicationBuilder(serviceProvder);
         }
+
+        private class MyComponent : IComponent
+        {
+            public void Attach(RenderHandle renderHandle)
+            {
+                throw new System.NotImplementedException();
+            }
+
+            public Task SetParametersAsync(ParameterView parameters)
+            {
+                throw new System.NotImplementedException();
+            }
+        }
     }
 }

File diff suppressed because it is too large
+ 0 - 0
src/Components/Web.JS/dist/Release/blazor.server.js


File diff suppressed because it is too large
+ 0 - 0
src/Components/Web.JS/dist/Release/blazor.webassembly.js


+ 4 - 30
src/Components/Web.JS/src/Boot.Server.ts

@@ -6,7 +6,7 @@ import { shouldAutoStart } from './BootCommon';
 import { RenderQueue } from './Platform/Circuits/RenderQueue';
 import { ConsoleLogger } from './Platform/Logging/Loggers';
 import { LogLevel, Logger } from './Platform/Logging/Logger';
-import { discoverPrerenderedCircuits, startCircuit } from './Platform/Circuits/CircuitManager';
+import { startCircuit } from './Platform/Circuits/CircuitManager';
 import { setEventDispatcher } from './Rendering/RendererEventDispatcher';
 import { resolveOptions, BlazorOptions } from './Platform/Circuits/BlazorOptions';
 import { DefaultReconnectionHandler } from './Platform/Circuits/DefaultReconnectionHandler';
@@ -27,32 +27,18 @@ async function boot(userOptions?: Partial<BlazorOptions>): Promise<void> {
   options.reconnectionHandler = options.reconnectionHandler || window['Blazor'].defaultReconnectionHandler;
   logger.log(LogLevel.Information, 'Starting up blazor server-side application.');
 
-  // Initialize statefully prerendered circuits and their components
-  // Note: This will all be removed soon
   const initialConnection = await initializeConnection(options, logger);
-  const circuits = discoverPrerenderedCircuits(document);
-  for (let i = 0; i < circuits.length; i++) {
-    const circuit = circuits[i];
-    for (let j = 0; j < circuit.components.length; j++) {
-      const component = circuit.components[j];
-      component.initialize();
-    }
-  }
-
   const circuit = await startCircuit(initialConnection);
-  if (!circuit) {
-    logger.log(LogLevel.Information, 'No preregistered components to render.');
-  }
 
   const reconnect = async (existingConnection?: signalR.HubConnection): Promise<boolean> => {
     if (renderingFailed) {
       // We can't reconnect after a failure, so exit early.
       return false;
     }
-    const reconnection = existingConnection || await initializeConnection(options, logger);
-    const results = await Promise.all(circuits.map(circuit => circuit.reconnect(reconnection)));
 
-    if (reconnectionFailed(results)) {
+    const reconnection = existingConnection || await initializeConnection(options, logger);
+    if (!(await circuit.reconnect(reconnection))) {
+      logger.log(LogLevel.Information, 'Reconnection attempt failed.');
       return false;
     }
 
@@ -63,19 +49,7 @@ async function boot(userOptions?: Partial<BlazorOptions>): Promise<void> {
 
   window['Blazor'].reconnect = reconnect;
 
-  const reconnectTask = reconnect(initialConnection);
-
-  if (circuit) {
-    circuits.push(circuit);
-  }
-
-  await reconnectTask;
-
   logger.log(LogLevel.Information, 'Blazor server-side application started.');
-
-  function reconnectionFailed(results: boolean[]): boolean {
-    return !results.reduce((current, next) => current && next, true);
-  }
 }
 
 async function initializeConnection(options: BlazorOptions, logger: Logger): Promise<signalR.HubConnection> {

+ 4 - 112
src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts

@@ -1,14 +1,10 @@
 import { internalFunctions as uriHelperFunctions } from '../../Services/UriHelper';
-import { ComponentDescriptor, MarkupRegistrationTags, StartComponentComment, EndComponentComment } from './ComponentDescriptor';
 
 export class CircuitDescriptor {
   public circuitId: string;
 
-  public components: ComponentDescriptor[];
-
-  public constructor(circuitId: string, components: ComponentDescriptor[]) {
+  public constructor(circuitId: string) {
     this.circuitId = circuitId;
-    this.components = components;
   }
 
   public reconnect(reconnection: signalR.HubConnection): Promise<boolean> {
@@ -16,115 +12,11 @@ export class CircuitDescriptor {
   }
 }
 
-
-export function discoverPrerenderedCircuits(document: Document): CircuitDescriptor[] {
-  const commentPairs = resolveCommentPairs(document);
-  const discoveredCircuits = new Map<string, ComponentDescriptor[]>();
-  for (let i = 0; i < commentPairs.length; i++) {
-    const pair = commentPairs[i];
-    // We replace '--' on the server with '..' when we prerender due to the fact that this
-    // is not allowed in HTML comments and doesn't get encoded by default.
-    const circuitId = pair.start.circuitId.replace('..', '--');
-    let circuit = discoveredCircuits.get(circuitId);
-    if (!circuit) {
-      circuit = [];
-      discoveredCircuits.set(circuitId, circuit);
-    }
-    const entry = new ComponentDescriptor(pair.start.componentId, circuitId, pair.start.rendererId, pair);
-    circuit.push(entry);
-  }
-  const circuits: CircuitDescriptor[] = [];
-  for (const [key, values] of discoveredCircuits) {
-    circuits.push(new CircuitDescriptor(key, values));
-  }
-  return circuits;
-}
-
-export async function startCircuit(connection: signalR.HubConnection): Promise<CircuitDescriptor | undefined> {
+export async function startCircuit(connection: signalR.HubConnection): Promise<CircuitDescriptor> {
   const result = await connection.invoke<string>('StartCircuit', uriHelperFunctions.getLocationHref(), uriHelperFunctions.getBaseURI());
   if (result) {
-    return new CircuitDescriptor(result, []);
+    return new CircuitDescriptor(result);
   } else {
-    return undefined;
-  }
-}
-
-function resolveCommentPairs(node: Node): MarkupRegistrationTags[] {
-  if (!node.hasChildNodes()) {
-    return [];
-  }
-  const result: MarkupRegistrationTags[] = [];
-  const children = node.childNodes;
-  let i = 0;
-  const childrenLength = children.length;
-  while (i < childrenLength) {
-    const currentChildNode = children[i];
-    const startComponent = getComponentStartComment(currentChildNode);
-    if (!startComponent) {
-      i++;
-      const childResults = resolveCommentPairs(currentChildNode);
-      for (let j = 0; j < childResults.length; j++) {
-        const childResult = childResults[j];
-        result.push(childResult);
-      }
-      continue;
-    }
-    const endComponent = getComponentEndComment(startComponent, children, i + 1, childrenLength);
-    result.push({ start: startComponent, end: endComponent });
-    i = endComponent.index + 1;
-  }
-  return result;
-}
-function getComponentStartComment(node: Node): StartComponentComment | undefined {
-  if (node.nodeType !== Node.COMMENT_NODE) {
-    return;
-  }
-  if (node.textContent) {
-    const componentStartComment = /\W+M.A.C.Component:[^{]*(.*)$/;
-    const definition = componentStartComment.exec(node.textContent);
-    const json = definition && definition[1];
-    if (json) {
-      try {
-        const { componentId, rendererId, circuitId } = JSON.parse(json);
-        const allComponents = componentId !== undefined && rendererId !== undefined && !!circuitId;
-        if (allComponents) {
-          return {
-            node: node as Comment,
-            circuitId,
-            rendererId: rendererId,
-            componentId: componentId,
-          };
-        }
-      } catch (error) {
-      }
-      throw new Error(`Found malformed start component comment at ${node.textContent}`);
-    }
-  }
-}
-function getComponentEndComment(component: StartComponentComment, children: NodeList, index: number, end: number): EndComponentComment {
-  for (let i = index; i < end; i++) {
-    const node = children[i];
-    if (node.nodeType !== Node.COMMENT_NODE) {
-      continue;
-    }
-    if (!node.textContent) {
-      continue;
-    }
-    const componentEndComment = /\W+M.A.C.Component:\W+(\d+)\W+$/;
-    const definition = componentEndComment.exec(node.textContent);
-    const json = definition && definition[1];
-    if (!json) {
-      continue;
-    }
-    try {
-      // The value is expected to be a JSON encoded number
-      const componentId = JSON.parse(json);
-      if (componentId === component.componentId) {
-        return { componentId, node: node as Comment, index: i };
-      }
-    } catch (error) {
-    }
-    throw new Error(`Found malformed end component comment at ${node.textContent}`);
+    throw new Error('Circuit failed to start');
   }
-  throw new Error(`End component comment not found for ${component.node}`);
 }

+ 0 - 46
src/Components/Web.JS/src/Platform/Circuits/ComponentDescriptor.ts

@@ -1,46 +0,0 @@
-import { attachRootComponentToLogicalElement } from '../../Rendering/Renderer';
-import { toLogicalRootCommentElement } from '../../Rendering/LogicalElements';
-
-export interface EndComponentComment {
-  componentId: number;
-  node: Comment;
-  index: number;
-}
-
-export interface StartComponentComment {
-  node: Comment;
-  rendererId: number;
-  componentId: number;
-  circuitId: string;
-}
-
-// Represent pairs of start end comments indicating a component that was registered
-// in markup (such as a prerendered component)
-export interface MarkupRegistrationTags {
-  start: StartComponentComment;
-  end: EndComponentComment;
-}
-
-export class ComponentDescriptor {
-  public registrationTags: MarkupRegistrationTags;
-
-  public componentId: number;
-
-  public circuitId: string;
-
-  public rendererId: number;
-
-  public constructor(componentId: number, circuitId: string, rendererId: number, descriptor: MarkupRegistrationTags) {
-    this.componentId = componentId;
-    this.circuitId = circuitId;
-    this.rendererId = rendererId;
-    this.registrationTags = descriptor;
-  }
-
-  public initialize(): void {
-    const startEndPair = { start: this.registrationTags.start.node, end: this.registrationTags.end.node };
-
-    const logicalElement = toLogicalRootCommentElement(startEndPair.start, startEndPair.end);
-    attachRootComponentToLogicalElement(this.rendererId, logicalElement, this.componentId);
-  }
-}

+ 0 - 146
src/Components/Web.JS/tests/CircuitManager.test.ts

@@ -1,146 +0,0 @@
-(global as any).DotNet = { attachReviver: jest.fn() };
-
-import { discoverPrerenderedCircuits } from '../src/Platform/Circuits/CircuitManager';
-import { JSDOM } from 'jsdom';
-
-describe('CircuitManager', () => {
-
-  it('discoverPrerenderedCircuits returns discovered prerendered circuits', () => {
-    const dom = new JSDOM(`<!doctype HTML>
-    <html>
-      <head>
-        <title>Page</title>
-      </head>
-      <body>
-        <header>Preamble</header>
-        <!-- M.A.C.Component: {"circuitId":"1234","rendererId":2,"componentId":1} -->
-        <p>Prerendered content</p>
-        <!-- M.A.C.Component: 1 -->
-        <footer></footer>
-      </body>
-    </html>`);
-
-    const results = discoverPrerenderedCircuits(dom.window.document);
-
-    expect(results.length).toEqual(1);
-    expect(results[0].components.length).toEqual(1);
-    const result = results[0].components[0];
-    expect(result.circuitId).toEqual("1234");
-    expect(result.rendererId).toEqual(2);
-    expect(result.componentId).toEqual(1);
-
-  });
-
-  it('discoverPrerenderedCircuits returns discovers multiple prerendered circuits', () => {
-    const dom = new JSDOM(`<!doctype HTML>
-    <html>
-      <head>
-        <title>Page</title>
-      </head>
-      <body>
-        <header>Preamble</header>
-        <!-- M.A.C.Component: {"circuitId":"1234","rendererId":2,"componentId":1} -->
-        <p>Prerendered content</p>
-        <!-- M.A.C.Component: 1 -->
-        <footer>
-          <!-- M.A.C.Component: {"circuitId":"1234","rendererId":2,"componentId":2} -->
-          <p>Prerendered content</p>
-          <!-- M.A.C.Component: 2 -->
-        </footer>
-      </body>
-    </html>`);
-
-    const results = discoverPrerenderedCircuits(dom.window.document);
-
-    expect(results.length).toEqual(1);
-    expect(results[0].components.length).toEqual(2);
-    const first = results[0].components[0];
-    expect(first.circuitId).toEqual("1234");
-    expect(first.rendererId).toEqual(2);
-    expect(first.componentId).toEqual(1);
-
-    const second = results[0].components[1];
-    expect(second.circuitId).toEqual("1234");
-    expect(second.rendererId).toEqual(2);
-    expect(second.componentId).toEqual(2);
-  });
-
-  it('discoverPrerenderedCircuits throws for malformed circuits - improper nesting', () => {
-    const dom = new JSDOM(`<!doctype HTML>
-    <html>
-      <head>
-        <title>Page</title>
-      </head>
-      <body>
-        <header>Preamble</header>
-        <!-- M.A.C.Component: {"circuitId":"1234","rendererId":2,"componentId":1} -->
-        <p>Prerendered content</p>
-        <!-- M.A.C.Component: 2 -->
-        <footer>
-        <!-- M.A.C.Component: {"circuitId":"1234","rendererId":2,"componentId":2} -->
-        <p>Prerendered content</p>
-        <!-- M.A.C.Component: 1 -->
-        </footer>
-      </body>
-    </html>`);
-
-    expect(() => discoverPrerenderedCircuits(dom.window.document))
-      .toThrow();
-  });
-
-
-  it('discoverPrerenderedCircuits throws for malformed circuits - mixed string and int', () => {
-    const dom = new JSDOM(`<!doctype HTML>
-    <html>
-      <head>
-        <title>Page</title>
-      </head>
-      <body>
-        <header>Preamble</header>
-        <!-- M.A.C.Component: {"circuitId":"1234","rendererId":"2","componentId":"1"} -->
-        <p>Prerendered content</p>
-        <!-- M.A.C.Component: 1 -->
-        <footer>
-        <!-- M.A.C.Component: {"circuitId":"1234","rendererId":2,"componentId":2} -->
-        <p>Prerendered content</p>
-        <!-- M.A.C.Component: 2 -->
-        </footer>
-      </body>
-    </html>`);
-
-    expect(() => discoverPrerenderedCircuits(dom.window.document))
-      .toThrow();
-  });
-
-  it('discoverPrerenderedCircuits initializes circuits', () => {
-    const dom = new JSDOM(`<!doctype HTML>
-    <html>
-      <head>
-        <title>Page</title>
-      </head>
-      <body>
-        <header>Preamble</header>
-        <!-- M.A.C.Component: {"circuitId":"1234","rendererId":2,"componentId":1} -->
-        <p>Prerendered content</p>
-        <!-- M.A.C.Component: 1 -->
-        <footer>
-          <!-- M.A.C.Component: {"circuitId":"1234","rendererId":2,"componentId":2} -->
-          <p>Prerendered content</p>
-          <!-- M.A.C.Component: 2 -->
-        </footer>
-      </body>
-    </html>`);
-
-    const results = discoverPrerenderedCircuits(dom.window.document);
-
-    for (let i = 0; i < results.length; i++) {
-      const result = results[i];
-      for (let j = 0; j < result.components.length; j++) {
-        const component = result.components[j];
-        component.initialize();
-      }
-    }
-
-  });
-
-});

+ 5 - 2
src/Components/test/E2ETest/ServerExecutionTests/ServerSideAppTest.cs

@@ -184,11 +184,14 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
                 _ => element.Text != currentValue);
         }
 
+        // Since we've removed stateful prerendering, the name which is passed in
+        // during prerendering cannot be retained. The first interactive render
+        // will remove it.
         [Fact]
-        public void RendersContinueAfterPrerendering()
+        public void RendersDoNotPreserveState()
         {
             Browser.FindElement(By.LinkText("Greeter")).Click();
-            Browser.Equal("Hello Guest", () => Browser.FindElement(By.ClassName("greeting")).Text);
+            Browser.Equal("Hello", () => Browser.FindElement(By.ClassName("greeting")).Text);
         }
 
         [Fact]

+ 0 - 1
src/Components/test/testassets/ComponentsApp.Server/ComponentsApp.Server.csproj

@@ -6,7 +6,6 @@
 
   <ItemGroup>
     <Reference Include="Microsoft.AspNetCore" />
-    <Reference Include="Microsoft.AspNetCore.Mvc.Components.Prerendering" />
     <Reference Include="Microsoft.AspNetCore.Components.Server" />
     <Reference Include="Microsoft.AspNetCore.Mvc" />
     <Reference Include="Newtonsoft.Json" />

+ 0 - 0
src/Components/test/testassets/ComponentsApp.Server/Pages/Index.cshtml → src/Components/test/testassets/ComponentsApp.Server/Pages/_Host.cshtml


+ 2 - 3
src/Components/test/testassets/ComponentsApp.Server/Startup.cs

@@ -2,7 +2,6 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Components.Server;
 using Microsoft.AspNetCore.Components.Server.Circuits;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.Extensions.DependencyInjection;
@@ -40,8 +39,8 @@ namespace ComponentsApp.Server
             {
                 endpoints.MapRazorPages();
                 endpoints.MapControllers();
-                endpoints.MapBlazorHub();
-                endpoints.MapFallbackToPage("/Index");
+                endpoints.MapBlazorHub<ComponentsApp.App.App>("app");
+                endpoints.MapFallbackToPage("/_Host");
             });
         }
     }

+ 2 - 16
src/Components/test/testassets/Ignitor/Program.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Net.Http;
+using System.Runtime.InteropServices;
 using System.Text.Json;
 using System.Text.RegularExpressions;
 using System.Threading;
@@ -75,8 +76,6 @@ namespace Ignitor
 
         public async Task ExecuteAsync(Uri uri)
         {
-            string circuitId = await GetPrerenderedCircuitId(uri);
-
             var builder = new HubConnectionBuilder();
             builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IHubProtocol, IgnitorMessagePackHubProtocol>());
             builder.WithUrl(new Uri(uri, "_blazor/"));
@@ -93,7 +92,7 @@ namespace Ignitor
             connection.Closed += OnClosedAsync;
 
             // Now everything is registered so we can start the circuit.
-            var success = await connection.InvokeAsync<bool>("ConnectCircuit", circuitId);
+            var success = await connection.InvokeAsync<bool>("StartCircuit", uri.AbsoluteUri, uri.GetLeftPart(UriPartial.Authority));
 
             await TaskCompletionSource.Task;
 
@@ -131,19 +130,6 @@ namespace Ignitor
             }
         }
 
-        private static async Task<string> GetPrerenderedCircuitId(Uri uri)
-        {
-            var httpClient = new HttpClient();
-            var response = await httpClient.GetAsync(uri);
-            var content = await response.Content.ReadAsStringAsync();
-
-            // <!-- M.A.C.Component:{"circuitId":"CfDJ8KZCIaqnXmdF...PVd6VVzfnmc1","rendererId":"0","componentId":"0"} -->
-            var match = Regex.Match(content, $"{Regex.Escape("<!-- M.A.C.Component:")}(.+?){Regex.Escape(" -->")}");
-            var json = JsonDocument.Parse(match.Groups[1].Value);
-            var circuitId = json.RootElement.GetProperty("circuitId").GetString();
-            return circuitId;
-        }
-
         private static async Task ClickAsync(string id, ElementHive hive, HubConnection connection)
         {
             if (!hive.TryFindElementById(id, out var elementNode))

+ 3 - 3
src/Components/test/testassets/TestServer/Components.TestServer.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
+<Project Sdk="Microsoft.NET.Sdk.Web">
 
   <PropertyGroup>
     <TargetFramework>netcoreapp3.0</TargetFramework>
@@ -8,11 +8,11 @@
     <Reference Include="Microsoft.AspNetCore" />
     <Reference Include="Microsoft.AspNetCore.Authentication.Cookies" />
     <Reference Include="Microsoft.AspNetCore.Blazor.Server" />
+    <Reference Include="Microsoft.AspNetCore.Components.Server" />
     <Reference Include="Microsoft.AspNetCore.Cors" />
     <Reference Include="Microsoft.AspNetCore.Mvc" />
     <Reference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" />
-    <Reference Include="Microsoft.AspNetCore.Components.Server" />
-    <Reference Include="Microsoft.AspNetCore.Mvc.Components.Prerendering" />
+    <Reference Include="Microsoft.AspNetCore.Mvc.ViewFeatures" />
     <Reference Include="Microsoft.Extensions.Logging.Testing" />
   </ItemGroup>
 

+ 1 - 1
src/Components/test/testassets/TestServer/Pages/PrerenderedHost.cshtml

@@ -7,7 +7,7 @@
     <base href="~/" />
 </head>
 <body>
-    <app>@(await Html.RenderStaticComponentAsync<TestRouter>())</app>
+    <app>@(await Html.RenderComponentAsync<TestRouter>())</app>
 
     @*
         So that E2E tests can make assertions about both the prerendered and

+ 0 - 11
src/Mvc/Mvc.Components.Prerendering/ref/Microsoft.AspNetCore.Mvc.Components.Prerendering.csproj

@@ -1,11 +0,0 @@
-<!-- This file is automatically generated. -->
-<Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <TargetFrameworks>netcoreapp3.0</TargetFrameworks>
-  </PropertyGroup>
-  <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.0'">
-    <Compile Include="Microsoft.AspNetCore.Mvc.Components.Prerendering.netcoreapp3.0.cs" />
-    <Reference Include="Microsoft.AspNetCore.Mvc.ViewFeatures"  />
-    <Reference Include="Microsoft.AspNetCore.Components.Server"  />
-  </ItemGroup>
-</Project>

+ 0 - 12
src/Mvc/Mvc.Components.Prerendering/ref/Microsoft.AspNetCore.Mvc.Components.Prerendering.netcoreapp3.0.cs

@@ -1,12 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-namespace Microsoft.AspNetCore.Mvc.Rendering
-{
-    public static partial class HtmlHelperComponentPrerenderingExtensions
-    {
-        public static System.Threading.Tasks.Task<Microsoft.AspNetCore.Html.IHtmlContent> RenderComponentAsync<TComponent>(this Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper htmlHelper) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; }
-        [System.Diagnostics.DebuggerStepThroughAttribute]
-        public static System.Threading.Tasks.Task<Microsoft.AspNetCore.Html.IHtmlContent> RenderComponentAsync<TComponent>(this Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper htmlHelper, object parameters) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; }
-    }
-}

+ 0 - 25
src/Mvc/Mvc.Components.Prerendering/src/HtmlContentPrerenderComponentResultAdapter.cs

@@ -1,25 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.IO;
-using System.Text.Encodings.Web;
-using Microsoft.AspNetCore.Components.Server;
-using Microsoft.AspNetCore.Html;
-
-namespace Microsoft.AspNetCore.Mvc.ViewFeatures
-{
-    internal class HtmlContentPrerenderComponentResultAdapter : IHtmlContent
-    {
-        private ComponentPrerenderResult _result;
-
-        public HtmlContentPrerenderComponentResultAdapter(ComponentPrerenderResult result)
-        {
-            _result = result;
-        }
-
-        public void WriteTo(TextWriter writer, HtmlEncoder encoder)
-        {
-            _result.WriteTo(writer);
-        }
-    }
-}

+ 0 - 75
src/Mvc/Mvc.Components.Prerendering/src/HtmlHelperComponentPrerenderingExtensions.cs

@@ -1,75 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Components;
-using Microsoft.AspNetCore.Components.Server;
-using Microsoft.AspNetCore.Html;
-using Microsoft.AspNetCore.Mvc.ViewFeatures;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.AspNetCore.Mvc.Rendering
-{
-    /// <summary>
-    /// Extensions for rendering components.
-    /// </summary>
-    public static class HtmlHelperComponentPrerenderingExtensions
-    {
-        /// <summary>
-        /// Renders the <typeparamref name="TComponent"/> <see cref="IComponent"/>.
-        /// </summary>
-        /// <param name="htmlHelper">The <see cref="IHtmlHelper"/>.</param>
-        /// <returns>The HTML produced by the rendered <typeparamref name="TComponent"/>.</returns>
-        public static Task<IHtmlContent> RenderComponentAsync<TComponent>(this IHtmlHelper htmlHelper) where TComponent : IComponent
-        {
-            if (htmlHelper == null)
-            {
-                throw new ArgumentNullException(nameof(htmlHelper));
-            }
-
-            return htmlHelper.RenderComponentAsync<TComponent>(null);
-        }
-
-        /// <summary>
-        /// Renders the <typeparamref name="TComponent"/> <see cref="IComponent"/>.
-        /// </summary>
-        /// <param name="htmlHelper">The <see cref="IHtmlHelper"/>.</param>
-        /// <param name="parameters">An <see cref="object"/> containing the parameters to pass
-        /// to the component.</param>
-        /// <returns>The HTML produced by the rendered <typeparamref name="TComponent"/>.</returns>
-        public static async Task<IHtmlContent> RenderComponentAsync<TComponent>(
-            this IHtmlHelper htmlHelper,
-            object parameters) where TComponent : IComponent
-        {
-            if (htmlHelper == null)
-            {
-                throw new ArgumentNullException(nameof(htmlHelper));
-            }
-
-            var httpContext = htmlHelper.ViewContext.HttpContext;
-            var serviceProvider = httpContext.RequestServices;
-            var prerenderer = serviceProvider.GetService<IComponentPrerenderer>();
-
-            if (prerenderer == null)
-            {
-                throw new InvalidOperationException($"No '{typeof(IComponentPrerenderer).Name}' implementation has been registered in the dependency injection container. " +
-                    $"This typically means a call to 'services.AddServerSideBlazor()' is missing in 'Startup.ConfigureServices'.");
-            }
-
-            var parametersCollection = parameters == null ?
-                ParameterView.Empty :
-                ParameterView.FromDictionary(HtmlHelper.ObjectToDictionary(parameters));
-
-            var result = await prerenderer.PrerenderComponentAsync(
-                new ComponentPrerenderingContext
-                {
-                    ComponentType = typeof(TComponent),
-                    Parameters = parametersCollection,
-                    Context = httpContext
-                });
-
-            return new HtmlContentPrerenderComponentResultAdapter(result);
-        }
-    }
-}

+ 0 - 18
src/Mvc/Mvc.Components.Prerendering/src/Microsoft.AspNetCore.Mvc.Components.Prerendering.csproj

@@ -1,18 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <Description>ASP.NET Core MVC component interactive rendering features. Contains types to integrate server-side rendered components into MVC Views and Pages.
-    </Description>
-    <TargetFramework>netcoreapp3.0</TargetFramework>
-    <NoWarn>$(NoWarn);CS1591</NoWarn>
-    <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    <PackageTags>aspnetcore;aspnetcoremvc</PackageTags>
-    <IsAspNetCoreApp>true</IsAspNetCoreApp>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <Reference Include="Microsoft.AspNetCore.Mvc.ViewFeatures" />
-    <Reference Include="Microsoft.AspNetCore.Components.Server" />
-  </ItemGroup>
-
-</Project>

+ 0 - 512
src/Mvc/Mvc.Components.Prerendering/test/HtmlHelperComponentPrerenderingExtensionsTests.cs

@@ -1,512 +0,0 @@
-using System;
-using System.IO;
-using System.Text.Encodings.Web;
-using System.Text.RegularExpressions;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Components;
-using Microsoft.AspNetCore.Components.RenderTree;
-using Microsoft.AspNetCore.Html;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Http.Features;
-using Microsoft.AspNetCore.Mvc.Rendering;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.JSInterop;
-using Microsoft.Net.Http.Headers;
-using Moq;
-using Xunit;
-
-namespace Microsoft.AspNetCore.Mvc.ViewFeatures
-{
-    public class HtmlHelperComponentExtensionsTests
-    {
-        private static readonly Regex ContentWrapperRegex = new Regex(
-            "<!-- M.A.C.Component: {\"circuitId\":\"[^\"]+\",\"rendererId\":0,\"componentId\":0} -->(?<content>.*)<!-- M.A.C.Component: 0 -->",
-            RegexOptions.Compiled | RegexOptions.Singleline, TimeSpan.FromSeconds(1)); // Treat the entire input string as a single line
-
-        [Fact]
-        public async Task PrerenderComponentAsync_ThrowsInvalidOperationException_IfNoPrerendererHasBeenRegistered()
-        {
-            // Arrange
-            var helper = CreateHelper(null, s => { });
-            var writer = new StringWriter();
-            var expectedmessage = $"No 'IComponentPrerenderer' implementation has been registered in the dependency injection container. " +
-                    $"This typically means a call to 'services.AddServerSideBlazor()' is missing in 'Startup.ConfigureServices'.";
-
-            // Act & Assert
-            var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => helper.RenderComponentAsync<TestComponent>());
-
-            // Assert
-            Assert.Equal(expectedmessage, exception.Message);
-        }
-
-        [Fact]
-        public async Task CanRender_ParameterlessComponent()
-        {
-            // Arrange
-            var helper = CreateHelper();
-
-            // Act
-            var result = await helper.RenderComponentAsync<TestComponent>();
-            var unwrappedContent = GetUnwrappedContent(result);
-
-            // Assert
-            Assert.Equal("<h1>Hello world!</h1>", unwrappedContent);
-        }
-
-        [Fact]
-        public async Task CanRender_ComponentWithParametersObject()
-        {
-            // Arrange
-            var helper = CreateHelper();
-
-            // Act
-            var result = await helper.RenderComponentAsync<GreetingComponent>(new
-            {
-                Name = "Guest"
-            });
-
-            var unwrappedContent = GetUnwrappedContent(result);
-
-            // Assert
-            Assert.Equal("<p>Hello Guest!</p>", unwrappedContent);
-        }
-
-        [Fact]
-        public async Task CanCatch_ComponentWithSynchronousException()
-        {
-            // Arrange
-            var helper = CreateHelper();
-
-            // Act & Assert
-            var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => helper.RenderComponentAsync<ExceptionComponent>(new
-            {
-                IsAsync = false
-            }));
-
-            // Assert
-            Assert.Equal("Threw an exception synchronously", exception.Message);
-        }
-
-        [Fact]
-        public async Task CanCatch_ComponentWithAsynchronousException()
-        {
-            // Arrange
-            var helper = CreateHelper();
-
-            // Act & Assert
-            var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => helper.RenderComponentAsync<ExceptionComponent>(new
-            {
-                IsAsync = true
-            }));
-
-            // Assert
-            Assert.Equal("Threw an exception asynchronously", exception.Message);
-        }
-
-        [Fact]
-        public async Task Rendering_ComponentWithJsInteropThrows()
-        {
-            // Arrange
-            var helper = CreateHelper();
-
-            // Act & Assert
-            var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => helper.RenderComponentAsync<ExceptionComponent>(new
-            {
-                JsInterop = true
-            }));
-
-            // Assert
-            Assert.Equal("JavaScript interop calls cannot be issued at this time. This is because the component is being " +
-                    "prerendered and the page has not yet loaded in the browser or because the circuit is currently disconnected. " +
-                    "Components must wrap any JavaScript interop calls in conditional logic to ensure those interop calls are not " +
-                    "attempted during prerendering or while the client is disconnected.",
-                exception.Message);
-        }
-
-        [Fact]
-        public async Task UriHelperRedirect_ThrowsInvalidOperationException_WhenResponseHasAlreadyStarted()
-        {
-            // Arrange
-            var ctx = new DefaultHttpContext();
-            ctx.Request.Scheme = "http";
-            ctx.Request.Host = new HostString("localhost");
-            ctx.Request.PathBase = "/base";
-            ctx.Request.Path = "/path";
-            ctx.Request.QueryString = new QueryString("?query=value");
-            var responseMock = new Mock<IHttpResponseFeature>();
-            responseMock.Setup(r => r.HasStarted).Returns(true);
-            ctx.Features.Set(responseMock.Object);
-            var helper = CreateHelper(ctx);
-            var writer = new StringWriter();
-
-            // Act
-            var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => helper.RenderComponentAsync<RedirectComponent>(new
-            {
-                RedirectUri = "http://localhost/redirect"
-            }));
-
-            Assert.Equal("A navigation command was attempted during prerendering after the server already started sending the response. " +
-                            "Navigation commands can not be issued during server-side prerendering after the response from the server has started. Applications must buffer the" +
-                            "reponse and avoid using features like FlushAsync() before all components on the page have been rendered to prevent failed navigation commands.",
-                exception.Message);
-        }
-
-        [Fact]
-        public async Task HtmlHelper_Redirects_WhenComponentNavigates()
-        {
-            // Arrange
-            var ctx = new DefaultHttpContext();
-            ctx.Request.Scheme = "http";
-            ctx.Request.Host = new HostString("localhost");
-            ctx.Request.PathBase = "/base";
-            ctx.Request.Path = "/path";
-            ctx.Request.QueryString = new QueryString("?query=value");
-            var helper = CreateHelper(ctx);
-
-            // Act
-            await helper.RenderComponentAsync<RedirectComponent>(new
-            {
-                RedirectUri = "http://localhost/redirect"
-            });
-
-            // Assert
-            Assert.Equal(302, ctx.Response.StatusCode);
-            Assert.Equal("http://localhost/redirect", ctx.Response.Headers[HeaderNames.Location]);
-        }
-
-        [Fact]
-        public async Task HtmlHelper_AvoidsRendering_WhenNavigationHasHappened()
-        {
-            // Arrange
-            var ctx = new DefaultHttpContext();
-            ctx.Request.Scheme = "http";
-            ctx.Request.Host = new HostString("localhost");
-            ctx.Request.PathBase = "/base";
-            ctx.Request.Path = "/path";
-            ctx.Request.QueryString = new QueryString("?query=value");
-            var helper = CreateHelper(ctx);
-            var stringWriter = new StringWriter();
-
-            await helper.RenderComponentAsync<RedirectComponent>(new
-            {
-                RedirectUri = "http://localhost/redirect"
-            });
-
-            // Act
-            var result = await helper.RenderComponentAsync<GreetingComponent>(new { Name = "George" });
-
-            // Assert
-            Assert.NotNull(result);
-            result.WriteTo(stringWriter, HtmlEncoder.Default);
-            Assert.Equal("", stringWriter.ToString());
-        }
-
-        [Fact]
-        public async Task CanRender_AsyncComponent()
-        {
-            // Arrange
-            var helper = CreateHelper();
-            var expectedContent = @"<table>
-<thead>
-<tr>
-<th>Date</th>
-<th>Summary</th>
-<th>F</th>
-<th>C</th>
-</tr>
-</thead>
-<tbody>
-<tr>
-<td>06/05/2018</td>
-<td>Freezing</td>
-<td>33</td>
-<td>33</td>
-</tr>
-<tr>
-<td>07/05/2018</td>
-<td>Bracing</td>
-<td>57</td>
-<td>57</td>
-</tr>
-<tr>
-<td>08/05/2018</td>
-<td>Freezing</td>
-<td>9</td>
-<td>9</td>
-</tr>
-<tr>
-<td>09/05/2018</td>
-<td>Balmy</td>
-<td>4</td>
-<td>4</td>
-</tr>
-<tr>
-<td>10/05/2018</td>
-<td>Chilly</td>
-<td>29</td>
-<td>29</td>
-</tr>
-</tbody>
-</table>";
-
-            // Act
-            var result = await helper.RenderComponentAsync<AsyncComponent>();
-            var unwrappedContent = GetUnwrappedContent(result);
-
-            // Assert
-            Assert.Equal(expectedContent.Replace("\r\n", "\n"), unwrappedContent);
-        }
-
-        private string GetUnwrappedContent(IHtmlContent rawResult)
-        {
-            var writer = new StringWriter();
-            rawResult.WriteTo(writer, HtmlEncoder.Default);
-
-            return ContentWrapperRegex.Match(writer.ToString())
-                .Groups["content"].Value
-                .Replace("\r\n", "\n");
-        }
-
-        private class TestComponent : IComponent
-        {
-            private RenderHandle _renderHandle;
-
-            public void Attach(RenderHandle renderHandle)
-            {
-                _renderHandle = renderHandle;
-            }
-
-            public Task SetParametersAsync(ParameterView parameters)
-            {
-                _renderHandle.Render(builder =>
-                {
-                    builder.OpenElement(1, "h1");
-                    builder.AddContent(2, "Hello world!");
-                    builder.CloseElement();
-                });
-                return Task.CompletedTask;
-            }
-        }
-
-        private class RedirectComponent : ComponentBase
-        {
-            [Inject] IUriHelper UriHelper { get; set; }
-
-            [Parameter] public string RedirectUri { get; set; }
-
-            [Parameter] public bool Force { get; set; }
-
-            protected override void OnInitialized()
-            {
-                UriHelper.NavigateTo(RedirectUri, Force);
-            }
-        }
-
-        private class ExceptionComponent : ComponentBase
-        {
-            [Parameter] public bool IsAsync { get; set; }
-
-            [Parameter] public bool JsInterop { get; set; }
-
-            [Inject] IJSRuntime JsRuntime { get; set; }
-
-            protected override async Task OnParametersSetAsync()
-            {
-                if (JsInterop)
-                {
-                    await JsRuntime.InvokeAsync<int>("window.alert", "Interop!");
-                }
-
-                if (!IsAsync)
-                {
-                    throw new InvalidOperationException("Threw an exception synchronously");
-                }
-                else
-                {
-                    await Task.Yield();
-                    throw new InvalidOperationException("Threw an exception asynchronously");
-                }
-            }
-        }
-
-        private class GreetingComponent : ComponentBase
-        {
-            [Parameter] public string Name { get; set; }
-
-            protected override void OnParametersSet()
-            {
-                base.OnParametersSet();
-            }
-
-            protected override void BuildRenderTree(RenderTreeBuilder builder)
-            {
-                base.BuildRenderTree(builder);
-                builder.OpenElement(1, "p");
-                builder.AddContent(2, $"Hello {Name}!");
-                builder.CloseElement();
-            }
-        }
-
-        private class AsyncComponent : ComponentBase
-        {
-            private static WeatherRow[] _weatherData = new[]
-            {
-                new WeatherRow
-                {
-                    DateFormatted = "06/05/2018",
-                    TemperatureC = 1,
-                    Summary = "Freezing",
-                    TemperatureF = 33
-                },
-                new WeatherRow
-                {
-                    DateFormatted = "07/05/2018",
-                    TemperatureC = 14,
-                    Summary = "Bracing",
-                    TemperatureF = 57
-                },
-                new WeatherRow
-                {
-                    DateFormatted = "08/05/2018",
-                    TemperatureC = -13,
-                    Summary = "Freezing",
-                    TemperatureF = 9
-                },
-                new WeatherRow
-                {
-                    DateFormatted = "09/05/2018",
-                    TemperatureC = -16,
-                    Summary = "Balmy",
-                    TemperatureF = 4
-                },
-                new WeatherRow
-                {
-                    DateFormatted = "10/05/2018",
-                    TemperatureC = 2,
-                    Summary = "Chilly",
-                    TemperatureF = 29
-                }
-            };
-
-            public class WeatherRow
-            {
-                public string DateFormatted { get; set; }
-                public int TemperatureC { get; set; }
-                public string Summary { get; set; }
-                public int TemperatureF { get; set; }
-            }
-
-            public WeatherRow[] RowsToDisplay { get; set; }
-
-            protected override async Task OnParametersSetAsync()
-            {
-                // Simulate an async workflow.
-                await Task.Yield();
-                RowsToDisplay = _weatherData;
-            }
-
-            protected override void BuildRenderTree(RenderTreeBuilder builder)
-            {
-                base.BuildRenderTree(builder);
-
-                builder.OpenElement(0, "table");
-                builder.AddMarkupContent(1, "\n");
-                builder.OpenElement(2, "thead");
-                builder.AddMarkupContent(3, "\n");
-                builder.OpenElement(4, "tr");
-                builder.AddMarkupContent(5, "\n");
-
-                builder.OpenElement(6, "th");
-                builder.AddContent(7, "Date");
-                builder.CloseElement();
-                builder.AddMarkupContent(8, "\n");
-
-                builder.OpenElement(9, "th");
-                builder.AddContent(10, "Summary");
-                builder.CloseElement();
-                builder.AddMarkupContent(11, "\n");
-
-                builder.OpenElement(12, "th");
-                builder.AddContent(13, "F");
-                builder.CloseElement();
-                builder.AddMarkupContent(14, "\n");
-
-                builder.OpenElement(15, "th");
-                builder.AddContent(16, "C");
-                builder.CloseElement();
-                builder.AddMarkupContent(17, "\n");
-
-                builder.CloseElement();
-                builder.AddMarkupContent(18, "\n");
-                builder.CloseElement();
-                builder.AddMarkupContent(19, "\n");
-                builder.OpenElement(20, "tbody");
-                builder.AddMarkupContent(21, "\n");
-                if (RowsToDisplay != null)
-                {
-                    foreach (var element in RowsToDisplay)
-                    {
-                        builder.OpenElement(22, "tr");
-                        builder.AddMarkupContent(23, "\n");
-
-                        builder.OpenElement(24, "td");
-                        builder.AddContent(25, element.DateFormatted);
-                        builder.CloseElement();
-                        builder.AddMarkupContent(26, "\n");
-
-                        builder.OpenElement(27, "td");
-                        builder.AddContent(28, element.Summary);
-                        builder.CloseElement();
-                        builder.AddMarkupContent(29, "\n");
-
-                        builder.OpenElement(30, "td");
-                        builder.AddContent(31, element.TemperatureF);
-                        builder.CloseElement();
-                        builder.AddMarkupContent(32, "\n");
-
-                        builder.OpenElement(33, "td");
-                        builder.AddContent(34, element.TemperatureF);
-                        builder.CloseElement();
-                        builder.AddMarkupContent(35, "\n");
-
-                        builder.CloseElement();
-                        builder.AddMarkupContent(36, "\n");
-                    }
-                }
-
-                builder.CloseElement();
-                builder.AddMarkupContent(37, "\n");
-
-                builder.CloseElement();
-            }
-        }
-
-        private static IHtmlHelper CreateHelper(HttpContext ctx = null, Action<IServiceCollection> configureServices = null)
-        {
-            var services = new ServiceCollection();
-            services.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
-            services.AddLogging();
-            services.AddDataProtection();
-            services.AddSingleton(HtmlEncoder.Default);
-            configureServices = configureServices ?? (s => s.AddServerSideBlazor());
-            configureServices?.Invoke(services);
-
-            var helper = new Mock<IHtmlHelper>();
-            var context = ctx ?? new DefaultHttpContext();
-            context.RequestServices = services.BuildServiceProvider();
-            context.Request.Scheme = "http";
-            context.Request.Host = new HostString("localhost");
-            context.Request.PathBase = "/base";
-            context.Request.Path = "/path";
-            context.Request.QueryString = QueryString.FromUriComponent("?query=value");
-
-            helper.Setup(h => h.ViewContext)
-                .Returns(new ViewContext()
-                {
-                    HttpContext = context
-                });
-            return helper.Object;
-        }
-    }
-}

+ 0 - 11
src/Mvc/Mvc.Components.Prerendering/test/Microsoft.AspNetCore.Mvc.Components.Prerendering.Test.csproj

@@ -1,11 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <TargetFramework>netcoreapp3.0</TargetFramework>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\..\shared\Mvc.Views.TestCommon\Microsoft.AspNetCore.Mvc.Views.TestCommon.csproj" />
-    <ProjectReference Include="..\..\shared\Mvc.TestDiagnosticListener\Microsoft.AspNetCore.Mvc.TestDiagnosticListener.csproj" />
-  </ItemGroup>
-</Project>

+ 6 - 6
src/Mvc/Mvc.ViewFeatures/ref/Microsoft.AspNetCore.Mvc.ViewFeatures.netcoreapp3.0.cs

@@ -322,6 +322,12 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
         Rfc3339 = 0,
         CurrentCulture = 1,
     }
+    public static partial class HtmlHelperComponentExtensions
+    {
+        public static System.Threading.Tasks.Task<Microsoft.AspNetCore.Html.IHtmlContent> RenderComponentAsync<TComponent>(this Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper htmlHelper) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; }
+        [System.Diagnostics.DebuggerStepThroughAttribute]
+        public static System.Threading.Tasks.Task<Microsoft.AspNetCore.Html.IHtmlContent> RenderComponentAsync<TComponent>(this Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper htmlHelper, object parameters) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; }
+    }
     public static partial class HtmlHelperDisplayExtensions
     {
         public static Microsoft.AspNetCore.Html.IHtmlContent Display(this Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper htmlHelper, string expression) { throw null; }
@@ -465,12 +471,6 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
         public static System.Threading.Tasks.Task RenderPartialAsync(this Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper htmlHelper, string partialViewName, Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary viewData) { throw null; }
         public static System.Threading.Tasks.Task RenderPartialAsync(this Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper htmlHelper, string partialViewName, object model) { throw null; }
     }
-    public static partial class HtmlHelperRazorComponentExtensions
-    {
-        public static System.Threading.Tasks.Task<Microsoft.AspNetCore.Html.IHtmlContent> RenderStaticComponentAsync<TComponent>(this Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper htmlHelper) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; }
-        [System.Diagnostics.DebuggerStepThroughAttribute]
-        public static System.Threading.Tasks.Task<Microsoft.AspNetCore.Html.IHtmlContent> RenderStaticComponentAsync<TComponent>(this Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper htmlHelper, object parameters) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; }
-    }
     public static partial class HtmlHelperSelectExtensions
     {
         public static Microsoft.AspNetCore.Html.IHtmlContent DropDownList(this Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper htmlHelper, string expression) { throw null; }

+ 4 - 4
src/Mvc/Mvc.ViewFeatures/src/HtmlHelperRazorComponentExtensions.cs → src/Mvc/Mvc.ViewFeatures/src/HtmlHelperComponentExtensions.cs

@@ -14,21 +14,21 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
     /// <summary>
     /// Extensions for rendering components.
     /// </summary>
-    public static class HtmlHelperRazorComponentExtensions
+    public static class HtmlHelperComponentExtensions
     {
         /// <summary>
         /// Renders the <typeparamref name="TComponent"/> <see cref="IComponent"/>.
         /// </summary>
         /// <param name="htmlHelper">The <see cref="IHtmlHelper"/>.</param>
         /// <returns>The HTML produced by the rendered <typeparamref name="TComponent"/>.</returns>
-        public static Task<IHtmlContent> RenderStaticComponentAsync<TComponent>(this IHtmlHelper htmlHelper) where TComponent : IComponent
+        public static Task<IHtmlContent> RenderComponentAsync<TComponent>(this IHtmlHelper htmlHelper) where TComponent : IComponent
         {
             if (htmlHelper == null)
             {
                 throw new ArgumentNullException(nameof(htmlHelper));
             }
 
-            return htmlHelper.RenderStaticComponentAsync<TComponent>(null);
+            return htmlHelper.RenderComponentAsync<TComponent>(null);
         }
 
         /// <summary>
@@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
         /// <param name="parameters">An <see cref="object"/> containing the parameters to pass
         /// to the component.</param>
         /// <returns>The HTML produced by the rendered <typeparamref name="TComponent"/>.</returns>
-        public static async Task<IHtmlContent> RenderStaticComponentAsync<TComponent>(
+        public static async Task<IHtmlContent> RenderComponentAsync<TComponent>(
             this IHtmlHelper htmlHelper,
             object parameters) where TComponent : IComponent
         {

+ 8 - 9
src/Mvc/Mvc.ViewFeatures/test/HtmlHelperComponentExtensionsTests.cs

@@ -7,7 +7,6 @@ using System.Text.Encodings.Web;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Components;
 using Microsoft.AspNetCore.Components.RenderTree;
-using Microsoft.AspNetCore.Components.Server;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Http.Features;
 using Microsoft.AspNetCore.Mvc.Rendering;
@@ -32,7 +31,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Test
             var writer = new StringWriter();
 
             // Act
-            var result = await helper.RenderStaticComponentAsync<TestComponent>();
+            var result = await helper.RenderComponentAsync<TestComponent>();
             result.WriteTo(writer, HtmlEncoder.Default);
             var content = writer.ToString();
 
@@ -48,7 +47,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Test
             var writer = new StringWriter();
 
             // Act
-            var result = await helper.RenderStaticComponentAsync<GreetingComponent>(new
+            var result = await helper.RenderComponentAsync<GreetingComponent>(new
             {
                 Name = "Steve"
             });
@@ -66,7 +65,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Test
             var helper = CreateHelper();
 
             // Act & Assert
-            var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => helper.RenderStaticComponentAsync<ExceptionComponent>(new
+            var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => helper.RenderComponentAsync<ExceptionComponent>(new
             {
                 IsAsync = false
             }));
@@ -82,7 +81,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Test
             var helper = CreateHelper();
 
             // Act & Assert
-            var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => helper.RenderStaticComponentAsync<ExceptionComponent>(new
+            var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => helper.RenderComponentAsync<ExceptionComponent>(new
             {
                 IsAsync = true
             }));
@@ -98,7 +97,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Test
             var helper = CreateHelper();
 
             // Act & Assert
-            var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => helper.RenderStaticComponentAsync<ExceptionComponent>(new
+            var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => helper.RenderComponentAsync<ExceptionComponent>(new
             {
                 JsInterop = true
             }));
@@ -127,7 +126,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Test
             var writer = new StringWriter();
 
             // Act
-            var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => helper.RenderStaticComponentAsync<RedirectComponent>(new
+            var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => helper.RenderComponentAsync<RedirectComponent>(new
             {
                 RedirectUri = "http://localhost/redirect"
             }));
@@ -151,7 +150,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Test
             var helper = CreateHelper(ctx);
 
             // Act
-            await helper.RenderStaticComponentAsync<RedirectComponent>(new
+            await helper.RenderComponentAsync<RedirectComponent>(new
             {
                 RedirectUri = "http://localhost/redirect"
             });
@@ -211,7 +210,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Test
 </table>";
 
             // Act
-            var result = await helper.RenderStaticComponentAsync<AsyncComponent>();
+            var result = await helper.RenderComponentAsync<AsyncComponent>();
             result.WriteTo(writer, HtmlEncoder.Default);
             var content = writer.ToString();
 

+ 0 - 6
src/Mvc/Mvc.sln

@@ -313,12 +313,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Signal
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RazorBuildWebSite.PrecompiledViews", "test\WebSites\RazorBuildWebSite.PrecompiledViews\RazorBuildWebSite.PrecompiledViews.csproj", "{A8C3066F-E80D-4E03-9962-741B551B8FBC}"
 EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc.Components.Prerendering", "Mvc.Components.Prerendering", "{45CE788D-4B69-4F83-981C-F43D8F15B0F1}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Components.Prerendering", "Mvc.Components.Prerendering\src\Microsoft.AspNetCore.Mvc.Components.Prerendering.csproj", "{6D6489E5-48BD-4F9B-9EEE-22AEEA1E1890}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Components.Prerendering.Test", "Mvc.Components.Prerendering\test\Microsoft.AspNetCore.Mvc.Components.Prerendering.Test.csproj", "{C6F3BCE6-1EFD-4360-932B-B98573E78926}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.Protocols.Json", "..\SignalR\common\Protocols.Json\src\Microsoft.AspNetCore.SignalR.Protocols.Json.csproj", "{637119E8-5BBB-4FC7-A372-DAF38FF5EBD9}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions.ApiDescription.Server", "Extensions.ApiDescription.Server", "{C15AA245-9E54-4FD6-90FF-B46F47779C46}"

+ 1 - 3
src/Mvc/MvcNoDeps.slnf

@@ -65,9 +65,7 @@
        "Mvc.NewtonsoftJson\\test\\Microsoft.AspNetCore.Mvc.NewtonsoftJson.Test.csproj",
        "Mvc.Razor.RuntimeCompilation\\src\\Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.csproj",
        "Mvc.Razor.RuntimeCompilation\\test\\Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.Test.csproj",
-       "test\\WebSites\\RazorBuildWebSite.PrecompiledViews\\RazorBuildWebSite.PrecompiledViews.csproj",
-       "Mvc.Components.Prerendering\\src\\Microsoft.AspNetCore.Mvc.Components.Prerendering.csproj",
-       "Mvc.Components.Prerendering\\test\\Microsoft.AspNetCore.Mvc.Components.Prerendering.Test.csproj",
+       "test\\WebSites\\RazorBuildWebSite.PrecompiledViews\\RazorBuildWebSite.PrecompiledViews.csproj"
     ]
   }
 }

+ 0 - 1
src/Mvc/samples/MvcSandbox/MvcSandbox.csproj

@@ -6,7 +6,6 @@
 
   <ItemGroup>
     <Reference Include="Microsoft.AspNetCore.Mvc" />
-    <Reference Include="Microsoft.AspNetCore.Mvc.Components.Prerendering" />
     <Reference Include="Microsoft.AspNetCore.Components.Server" />
     <Reference Include="Microsoft.AspNetCore.Diagnostics" />
     <Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />

+ 0 - 1
src/Mvc/shared/Mvc.Views.TestCommon/Microsoft.AspNetCore.Mvc.Views.TestCommon.csproj

@@ -8,7 +8,6 @@
   <ItemGroup>
     <ProjectReference Include="..\Mvc.Core.TestCommon\Microsoft.AspNetCore.Mvc.Core.TestCommon.csproj" />
     <Reference Include="Microsoft.AspNetCore.Mvc.ViewFeatures" />
-    <Reference Include="Microsoft.AspNetCore.Mvc.Components.Prerendering" />
 
     <Reference Include="Microsoft.AspNetCore.Razor.Runtime" />
     <Reference Include="Microsoft.Extensions.WebEncoders" />

+ 0 - 1
src/Mvc/test/Mvc.FunctionalTests/BasicTests.cs

@@ -482,7 +482,6 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
             {
                 "BasicWebSite",
                 "Microsoft.AspNetCore.Components.Server",
-                "Microsoft.AspNetCore.Mvc.Components.Prerendering",
                 "Microsoft.AspNetCore.SpaServices",
                 "Microsoft.AspNetCore.SpaServices.Extensions",
                 "Microsoft.AspNetCore.Mvc.TagHelpers",

+ 6 - 77
src/Mvc/test/Mvc.FunctionalTests/ComponentRenderingFunctionalTests.cs

@@ -40,23 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
             await response.AssertStatusCodeAsync(HttpStatusCode.OK);
             var content = await response.Content.ReadAsStringAsync();
 
-            AssertComponent("\n<p>Hello world!</p>", "Greetings", content);
-        }
-
-        [Fact]
-        public async Task Renders_BasicComponent_UsingRazorComponents_Prerenderer()
-        {
-            // Arrange & Act
-            var client = CreateClient(Factory
-                .WithWebHostBuilder(builder => builder.ConfigureServices(services => services.AddServerSideBlazor())));
-
-            var response = await client.GetAsync("http://localhost/components");
-
-            // Assert
-            await response.AssertStatusCodeAsync(HttpStatusCode.OK);
-            var content = await response.Content.ReadAsStringAsync();
-
-            AssertComponent("\n<p>Hello world!</p>", "Greetings", content);
+            AssertComponent("<p>Hello world!</p>", "Greetings", content);
         }
 
         [Fact]
@@ -71,7 +55,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
             await response.AssertStatusCodeAsync(HttpStatusCode.OK);
             var content = await response.Content.ReadAsStringAsync();
 
-            AssertComponent("\nRouter component\n<p>Routed successfully</p>", "Routing", content);
+            AssertComponent("Router component\n<p>Routed successfully</p>", "Routing", content);
         }
 
         [Fact]
@@ -89,21 +73,6 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
             Assert.Equal("http://localhost/navigation-redirect", response.Headers.Location.ToString());
         }
 
-        [Fact]
-        public async Task Redirects_Navigation_ComponentInteractive()
-        {
-            // Arrange & Act
-            var fixture = Factory.WithWebHostBuilder(builder => builder.ConfigureServices(services => services.AddServerSideBlazor()));
-            fixture.ClientOptions.AllowAutoRedirect = false;
-            var client = CreateClient(fixture);
-
-            var response = await client.GetAsync("http://localhost/components/Navigation/false");
-
-            // Assert
-            await response.AssertStatusCodeAsync(HttpStatusCode.Redirect);
-            Assert.Equal("http://localhost/navigation-redirect", response.Headers.Location.ToString());
-        }
-
         [Fact]
         public async Task Renders_RoutingComponent_UsingRazorComponents_Prerenderer()
         {
@@ -117,39 +86,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
             await response.AssertStatusCodeAsync(HttpStatusCode.OK);
             var content = await response.Content.ReadAsStringAsync();
 
-            AssertComponent("\nRouter component\n<p>Routed successfully</p>", "Routing", content);
-        }
-
-        [Fact]
-        public async Task Renders_BasicComponentInteractive_UsingRazorComponents_Prerenderer()
-        {
-            // Arrange & Act
-            var client = CreateClient(Factory
-                .WithWebHostBuilder(builder => builder.ConfigureServices(services => services.AddServerSideBlazor())));
-
-            var response = await client.GetAsync("http://localhost/components/false");
-
-            // Assert
-            await response.AssertStatusCodeAsync(HttpStatusCode.OK);
-            var content = await response.Content.ReadAsStringAsync();
-
-            AssertComponent("<p>Hello world!</p>", "Greetings", content, unwrap: true);
-        }
-
-        [Fact]
-        public async Task Renders_RoutingComponentInteractive_UsingRazorComponents_Prerenderer()
-        {
-            // Arrange & Act
-            var client = CreateClient(Factory
-                .WithWebHostBuilder(builder => builder.ConfigureServices(services => services.AddServerSideBlazor())));
-
-            var response = await client.GetAsync("http://localhost/components/routable/false");
-
-            // Assert
-            await response.AssertStatusCodeAsync(HttpStatusCode.OK);
-            var content = await response.Content.ReadAsStringAsync();
-
-            AssertComponent("Router component\n<p>Routed successfully</p>", "Routing", content, unwrap: true);
+            AssertComponent("Router component\n<p>Routed successfully</p>", "Routing", content);
         }
 
         [Fact]
@@ -171,8 +108,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
         public async Task Renders_AsyncComponent()
         {
             // Arrange & Act
-            var expectedHtml = @"
-<h1>Weather forecast</h1>
+            var expectedHtml = @"<h1>Weather forecast</h1>
 
 <p>This component demonstrates fetching data from the server.</p>
 
@@ -230,24 +166,17 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
             AssertComponent(expectedHtml, "FetchData", content);
         }
 
-        private void AssertComponent(string expectedContent, string divId, string responseContent, bool unwrap = false)
+        private void AssertComponent(string expectedContent, string divId, string responseContent)
         {
             var parser = new HtmlParser();
             var htmlDocument = parser.Parse(responseContent);
             var div = htmlDocument.Body.QuerySelector($"#{divId}");
-            var content = unwrap ? GetUnwrappedContent(div.InnerHtml) : div.InnerHtml;
+            var content = div.InnerHtml;
             Assert.Equal(
                 expectedContent.Replace("\r\n","\n"),
                 content.Replace("\r\n","\n"));
         }
 
-        private string GetUnwrappedContent(string rawResult)
-        {
-            return ContentWrapperRegex.Match(rawResult)
-                .Groups["content"].Value
-                .Replace("\r\n", "\n");
-        }
-
         // A simple delegating handler used in setting up test services so that we can configure
         // services that talk back to the TestServer using HttpClient.
         private class LoopHttpHandler : DelegatingHandler

+ 0 - 1
src/Mvc/test/WebSites/BasicWebSite/BasicWebSite.csproj

@@ -15,7 +15,6 @@
     <Reference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" />
 
     <Reference Include="Microsoft.AspNetCore.Authentication" />
-    <Reference Include="Microsoft.AspNetCore.Mvc.Components.Prerendering" />
     <Reference Include="Microsoft.AspNetCore.Localization.Routing" />
     <Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
     <Reference Include="Microsoft.AspNetCore.Server.Kestrel" />

+ 7 - 11
src/Mvc/test/WebSites/BasicWebSite/Controllers/RazorComponentsController.cs

@@ -1,10 +1,6 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 
@@ -51,16 +47,16 @@ namespace BasicWebSite.Controllers
                 }
             };
 
-        [HttpGet("/components/{staticPrerender=true}")]
-        [HttpGet("/components/routable/{staticPrerender=true}")]
-        public IActionResult Index(bool staticPrerender)
+        [HttpGet("/components/{**slug}")]
+        [HttpGet("/components/routable/{**slug}")]
+        public IActionResult Index()
         {
             // Override the path so that the router finds the RoutedPage component
             // as the client router doesn't support optional parameters.
             Request.Path = Request.Path.StartsWithSegments("/components/routable") ?
                 PathString.FromUriComponent("/components/routable") : Request.Path;
 
-            return View(staticPrerender);
+            return View();
         }
 
         [HttpGet("/WeatherData")]
@@ -70,10 +66,10 @@ namespace BasicWebSite.Controllers
             return Ok(_weatherData);
         }
 
-        [HttpGet("/components/Navigation/{staticPrerender=true}")]
-        public IActionResult Navigation(bool staticPrerender)
+        [HttpGet("/components/Navigation")]
+        public IActionResult Navigation()
         {
-            return View(staticPrerender);
+            return View();
         }
 
         private class WeatherRow

+ 1 - 1
src/Mvc/test/WebSites/BasicWebSite/RazorComponents/FetchData.razor

@@ -50,4 +50,4 @@ else
 
         forecasts = await ForecastService.GetForecastAsync(StartDate);
     }
-}
+}

+ 4 - 31
src/Mvc/test/WebSites/BasicWebSite/Views/RazorComponents/Index.cshtml

@@ -1,35 +1,8 @@
 @using BasicWebSite.RazorComponents;
-@model bool;
+
 <h1>Razor components</h1>
-<div id="Greetings">
-    @if (Model)
-    {
-        @(await Html.RenderStaticComponentAsync<Greetings>())
-    }
-    else
-    {
-        @(await Html.RenderComponentAsync<Greetings>())
-    }
-</div>
+<div id="Greetings">@(await Html.RenderComponentAsync<Greetings>())</div>
 
-<div id="FetchData">
-    @if (Model)
-    {
-        @(await Html.RenderStaticComponentAsync<FetchData>(new { StartDate = new DateTime(2019, 01, 15) }))
-    }
-    else
-    {
-        @(await Html.RenderComponentAsync<FetchData>(new { StartDate = new DateTime(2019, 01, 15) }))
-    }
-</div>
+<div id="FetchData">@(await Html.RenderComponentAsync<FetchData>(new { StartDate = new DateTime(2019, 01, 15) }))</div>
 
-<div id="Routing">
-    @if (Model)
-    {
-        @(await Html.RenderStaticComponentAsync<RouterContainer>());
-    }
-    else
-    {
-        @(await Html.RenderComponentAsync<RouterContainer>());
-    }
-</div>
+<div id="Routing">@(await Html.RenderComponentAsync<RouterContainer>())</div>

+ 1 - 11
src/Mvc/test/WebSites/BasicWebSite/Views/RazorComponents/Navigation.cshtml

@@ -1,13 +1,3 @@
 @using BasicWebSite.RazorComponents;
-@model bool;
 <h1>Navigation components</h1>
-<div id="Navigation">
-    @if (Model)
-    {
-        @(await Html.RenderStaticComponentAsync<NavigationComponent>())
-    }
-    else
-    {
-        @(await Html.RenderComponentAsync<NavigationComponent>())
-    }
-</div>
+<div id="Navigation">@(await Html.RenderComponentAsync<NavigationComponent>())</div>

+ 0 - 2
src/ProjectTemplates/ProjectTemplates.sln

@@ -175,8 +175,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_dependencies", "_dependenc
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.ApiAuthorization.IdentityServer", "..\Identity\ApiAuthorization.IdentityServer\src\Microsoft.AspNetCore.ApiAuthorization.IdentityServer.csproj", "{6012D544-32B4-4F5C-B335-A224AA4F661D}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Components.Prerendering", "..\Mvc\Mvc.Components.Prerendering\src\Microsoft.AspNetCore.Mvc.Components.Prerendering.csproj", "{706DBAF7-0BB4-4B4F-9B44-E7C3372ECDF2}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SpaServices.Extensions", "..\Middleware\SpaServices.Extensions\src\Microsoft.AspNetCore.SpaServices.Extensions.csproj", "{06D0D7B2-EDA3-45A2-A060-AB791ED1DB80}"
 EndProject
 Global

+ 1 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Pages/_Host.cshtml

@@ -15,7 +15,7 @@
 <body>
     <app>
         @* Remove the following line of code to disable prerendering *@
-        @(await Html.RenderStaticComponentAsync<App>())
+        @(await Html.RenderComponentAsync<App>())
     </app>
 
     <script src="_framework/blazor.server.js"></script>

Some files were not shown because too many files changed in this diff