Răsfoiți Sursa

Support the new top level statements with WebApplicationFactory (#33462)

* Support the new top level statements with WebApplicationFactory
- This change adds support for the WebApplicationFactory without a CreateHostBuilder method. It uses the new ResolveHostFactory method in combination with a DeferredHostBuilder and DeferredHost to accomplish this.
- Look for both IHostBuilder and IWebHostBuilder before falling back to the new pattern.
- Added WebApplicationFactory test for top level statements
- Allow CreateWebHostBuilder to return nullable builder

Co-authored-by: Safia Abdalla <[email protected]>
Co-authored-by: Safia Abdalla <[email protected]>
David Fowler 4 ani în urmă
părinte
comite
1048a02802

+ 15 - 0
AspNetCore.sln

@@ -1612,6 +1612,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Compon
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebView.Test", "src\Components\WebView\WebView\test\Microsoft.AspNetCore.Components.WebView.Test.csproj", "{4BD6F0DB-BE9C-4C54-B52A-D20B88855ED5}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleWebSiteWithWebApplicationBuilder", "src\Mvc\test\WebSites\SimpleWebSiteWithWebApplicationBuilder\SimpleWebSiteWithWebApplicationBuilder.csproj", "{6CCCF618-2E70-4870-B39F-32C016FE08F0}"
+EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{B730328F-D9E9-4EAA-B28E-4631A14095F9}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PhotinoPlatform", "PhotinoPlatform", "{44963D50-8B58-44E6-918D-788BCB406695}"
@@ -7693,6 +7695,18 @@ Global
 		{4BD6F0DB-BE9C-4C54-B52A-D20B88855ED5}.Release|x64.Build.0 = Release|Any CPU
 		{4BD6F0DB-BE9C-4C54-B52A-D20B88855ED5}.Release|x86.ActiveCfg = Release|Any CPU
 		{4BD6F0DB-BE9C-4C54-B52A-D20B88855ED5}.Release|x86.Build.0 = Release|Any CPU
+		{6CCCF618-2E70-4870-B39F-32C016FE08F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6CCCF618-2E70-4870-B39F-32C016FE08F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6CCCF618-2E70-4870-B39F-32C016FE08F0}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{6CCCF618-2E70-4870-B39F-32C016FE08F0}.Debug|x64.Build.0 = Debug|Any CPU
+		{6CCCF618-2E70-4870-B39F-32C016FE08F0}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{6CCCF618-2E70-4870-B39F-32C016FE08F0}.Debug|x86.Build.0 = Debug|Any CPU
+		{6CCCF618-2E70-4870-B39F-32C016FE08F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6CCCF618-2E70-4870-B39F-32C016FE08F0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6CCCF618-2E70-4870-B39F-32C016FE08F0}.Release|x64.ActiveCfg = Release|Any CPU
+		{6CCCF618-2E70-4870-B39F-32C016FE08F0}.Release|x64.Build.0 = Release|Any CPU
+		{6CCCF618-2E70-4870-B39F-32C016FE08F0}.Release|x86.ActiveCfg = Release|Any CPU
+		{6CCCF618-2E70-4870-B39F-32C016FE08F0}.Release|x86.Build.0 = Release|Any CPU
 		{558C46DE-DE16-41D5-8DB7-D6D748E32977}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{558C46DE-DE16-41D5-8DB7-D6D748E32977}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{558C46DE-DE16-41D5-8DB7-D6D748E32977}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -8515,6 +8529,7 @@ Global
 		{A1D02CE6-1077-410A-81CB-D4BD500FD765} = {0508E463-0269-40C9-B5C2-3B600FB2A28B}
 		{3044DFA5-DE4F-44D8-8DD8-EDF547BE513E} = {C445B129-0A4D-41F5-8347-6534B6B12303}
 		{4BD6F0DB-BE9C-4C54-B52A-D20B88855ED5} = {C445B129-0A4D-41F5-8347-6534B6B12303}
+		{6CCCF618-2E70-4870-B39F-32C016FE08F0} = {088C37A5-30D2-40FB-B031-D163CFBED006}
 		{B730328F-D9E9-4EAA-B28E-4631A14095F9} = {C445B129-0A4D-41F5-8347-6534B6B12303}
 		{44963D50-8B58-44E6-918D-788BCB406695} = {B730328F-D9E9-4EAA-B28E-4631A14095F9}
 		{3EC71A0E-6515-4A5A-B759-F0BCF1BCFC56} = {44963D50-8B58-44E6-918D-788BCB406695}

+ 131 - 0
src/Mvc/Mvc.Testing/src/DeferredHostBuilder.cs

@@ -0,0 +1,131 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace Microsoft.AspNetCore.Mvc.Testing
+{
+    // This host builder captures calls to the IHostBuilder then replays them in the call to ConfigureHostBuilder
+    internal class DeferredHostBuilder : IHostBuilder
+    {
+        public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>();
+
+        private Action<IHostBuilder> _configure;
+        private Func<string[], object>? _hostFactory;
+
+        public DeferredHostBuilder()
+        {
+            _configure = b =>
+            {
+                // Copy the properties from this builder into the builder
+                // that we're going to receive
+                foreach (var pair in Properties)
+                {
+                    b.Properties[pair.Key] = pair.Value;
+                }
+            };
+        }
+
+        public IHost Build()
+        {
+            // This will never be null if the case where Build is being called
+            var host = (IHost)_hostFactory!(Array.Empty<string>());
+
+            // We can't return the host directly since we need to defer the call to StartAsync
+            return new DeferredHost(host);
+        }
+
+        public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
+        {
+            _configure += b => b.ConfigureAppConfiguration(configureDelegate);
+            return this;
+        }
+
+        public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate)
+        {
+            _configure += b => b.ConfigureContainer(configureDelegate);
+            return this;
+        }
+
+        public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate)
+        {
+            _configure += b => b.ConfigureHostConfiguration(configureDelegate);
+            return this;
+        }
+
+        public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
+        {
+            _configure += b => b.ConfigureServices(configureDelegate);
+            return this;
+        }
+
+        public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory) where TContainerBuilder : notnull
+        {
+            _configure += b => b.UseServiceProviderFactory(factory);
+            return this;
+        }
+
+        public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory) where TContainerBuilder : notnull
+        {
+            _configure += b => b.UseServiceProviderFactory(factory);
+            return this;
+        }
+
+        public void ConfigureHostBuilder(object hostBuilder)
+        {
+            _configure(((IHostBuilder)hostBuilder));
+        }
+
+        public void SetHostFactory(Func<string[], object> hostFactory)
+        {
+            _hostFactory = hostFactory;
+        }
+
+        private class DeferredHost : IHost, IAsyncDisposable
+        {
+            private readonly IHost _host;
+
+            public DeferredHost(IHost host)
+            {
+                _host = host;
+            }
+
+            public IServiceProvider Services => _host.Services;
+
+            public void Dispose() => _host.Dispose();
+
+            public ValueTask DisposeAsync()
+            {
+                if (_host is IAsyncDisposable disposable)
+                {
+                    return disposable.DisposeAsync();
+                }
+                Dispose();
+                return default;
+            }
+
+            public Task StartAsync(CancellationToken cancellationToken = default)
+            {
+                var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+
+                // Wait on the existing host to start running and have this call wait on that. This avoids starting the actual host too early and
+                // leaves the application in charge of calling start.
+
+                using var reg = cancellationToken.UnsafeRegister(_ => tcs.TrySetCanceled(), null);
+
+                // REVIEW: This will deadlock if the application creates the host but never calls start. This is mitigated by the cancellationToken
+                // but it's rarely a valid token for Start
+                _host.Services.GetRequiredService<IHostApplicationLifetime>().ApplicationStarted.UnsafeRegister(_ => tcs.TrySetResult(), null);
+
+                return tcs.Task;
+            }
+
+            public Task StopAsync(CancellationToken cancellationToken = default) => _host.StopAsync(cancellationToken);
+        }
+    }
+}

+ 1 - 1
src/Mvc/Mvc.Testing/src/PublicAPI.Unshipped.txt

@@ -49,6 +49,6 @@ virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Conf
 virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateHost(Microsoft.Extensions.Hosting.IHostBuilder! builder) -> Microsoft.Extensions.Hosting.IHost!
 virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateHostBuilder() -> Microsoft.Extensions.Hosting.IHostBuilder?
 virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateServer(Microsoft.AspNetCore.Hosting.IWebHostBuilder! builder) -> Microsoft.AspNetCore.TestHost.TestServer!
-virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateWebHostBuilder() -> Microsoft.AspNetCore.Hosting.IWebHostBuilder!
+virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateWebHostBuilder() -> Microsoft.AspNetCore.Hosting.IWebHostBuilder?
 virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.GetTestAssemblies() -> System.Collections.Generic.IEnumerable<System.Reflection.Assembly!>!
 virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Services.get -> System.IServiceProvider!

+ 56 - 31
src/Mvc/Mvc.Testing/src/WebApplicationFactory.cs

@@ -13,6 +13,7 @@ using System.Threading.Tasks;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Hosting.Server;
 using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyModel;
 using Microsoft.Extensions.Hosting;
@@ -149,23 +150,56 @@ namespace Microsoft.AspNetCore.Mvc.Testing
             EnsureDepsFile();
 
             var hostBuilder = CreateHostBuilder();
-            if (hostBuilder != null)
+            if (hostBuilder is not null)
             {
-                hostBuilder.ConfigureWebHost(webHostBuilder =>
-                {
-                    SetContentRoot(webHostBuilder);
-                    _configuration(webHostBuilder);
-                    webHostBuilder.UseTestServer();
-                });
-                _host = CreateHost(hostBuilder);
-                _server = (TestServer)_host.Services.GetRequiredService<IServer>();
+                ConfigureHostBuilder(hostBuilder);
                 return;
             }
 
             var builder = CreateWebHostBuilder();
-            SetContentRoot(builder);
-            _configuration(builder);
-            _server = CreateServer(builder);
+            if (builder is null)
+            {
+                var deferredHostBuilder = new DeferredHostBuilder();
+                // This helper call does the hard work to determine if we can fallback to diagnostic source events to get the host instance
+                var factory = HostFactoryResolver.ResolveHostFactory(typeof(TEntryPoint).Assembly, stopApplication: false, configureHostBuilder: deferredHostBuilder.ConfigureHostBuilder);
+
+                if (factory is not null)
+                {
+                    // If we have a valid factory it means the specified entry point's assembly can potentially resolve the IHost
+                    // so we set the factory on the DeferredHostBuilder so we can invoke it on the call to IHostBuilder.Build.
+                    deferredHostBuilder.SetHostFactory(factory);
+
+                    ConfigureHostBuilder(deferredHostBuilder);
+                    return;
+                }
+
+                throw new InvalidOperationException(Resources.FormatMissingBuilderMethod(
+                    nameof(IHostBuilder),
+                    nameof(IWebHostBuilder),
+                    typeof(TEntryPoint).Assembly.EntryPoint!.DeclaringType!.FullName,
+                    typeof(WebApplicationFactory<TEntryPoint>).Name,
+                    nameof(CreateHostBuilder),
+                    nameof(CreateWebHostBuilder)));
+            }
+            else
+            {
+                SetContentRoot(builder);
+                _configuration(builder);
+                _server = CreateServer(builder);
+            }
+        }
+
+        [MemberNotNull(nameof(_server))]
+        private void ConfigureHostBuilder(IHostBuilder hostBuilder)
+        {
+            hostBuilder.ConfigureWebHost(webHostBuilder =>
+            {
+                SetContentRoot(webHostBuilder);
+                _configuration(webHostBuilder);
+                webHostBuilder.UseTestServer();
+            });
+            _host = CreateHost(hostBuilder);
+            _server = (TestServer)_host.Services.GetRequiredService<IServer>();
         }
 
         private void SetContentRoot(IWebHostBuilder builder)
@@ -341,10 +375,8 @@ namespace Microsoft.AspNetCore.Mvc.Testing
         protected virtual IHostBuilder? CreateHostBuilder()
         {
             var hostBuilder = HostFactoryResolver.ResolveHostBuilderFactory<IHostBuilder>(typeof(TEntryPoint).Assembly)?.Invoke(Array.Empty<string>());
-            if (hostBuilder != null)
-            {
-                hostBuilder.UseEnvironment(Environments.Development);
-            }
+
+            hostBuilder?.UseEnvironment(Environments.Development);
             return hostBuilder;
         }
 
@@ -357,23 +389,16 @@ namespace Microsoft.AspNetCore.Mvc.Testing
         /// array as arguments.
         /// </remarks>
         /// <returns>A <see cref="IWebHostBuilder"/> instance.</returns>
-        protected virtual IWebHostBuilder CreateWebHostBuilder()
+        protected virtual IWebHostBuilder? CreateWebHostBuilder()
         {
             var builder = WebHostBuilderFactory.CreateFromTypesAssemblyEntryPoint<TEntryPoint>(Array.Empty<string>());
-            if (builder == null)
-            {
-                throw new InvalidOperationException(Resources.FormatMissingBuilderMethod(
-                    nameof(IHostBuilder),
-                    nameof(IWebHostBuilder),
-                    typeof(TEntryPoint).Assembly.EntryPoint!.DeclaringType!.FullName,
-                    typeof(WebApplicationFactory<TEntryPoint>).Name,
-                    nameof(CreateHostBuilder),
-                    nameof(CreateWebHostBuilder)));
-            }
-            else
+
+            if (builder is not null)
             {
                 return builder.UseEnvironment(Environments.Development);
             }
+
+            return null;
         }
 
         /// <summary>
@@ -566,7 +591,7 @@ namespace Microsoft.AspNetCore.Mvc.Testing
         {
             private readonly Func<IWebHostBuilder, TestServer> _createServer;
             private readonly Func<IHostBuilder, IHost> _createHost;
-            private readonly Func<IWebHostBuilder> _createWebHostBuilder;
+            private readonly Func<IWebHostBuilder?> _createWebHostBuilder;
             private readonly Func<IHostBuilder?> _createHostBuilder;
             private readonly Func<IEnumerable<Assembly>> _getTestAssemblies;
             private readonly Action<HttpClient> _configureClient;
@@ -575,7 +600,7 @@ namespace Microsoft.AspNetCore.Mvc.Testing
                 WebApplicationFactoryClientOptions options,
                 Func<IWebHostBuilder, TestServer> createServer,
                 Func<IHostBuilder, IHost> createHost,
-                Func<IWebHostBuilder> createWebHostBuilder,
+                Func<IWebHostBuilder?> createWebHostBuilder,
                 Func<IHostBuilder?> createHostBuilder,
                 Func<IEnumerable<Assembly>> getTestAssemblies,
                 Action<HttpClient> configureClient,
@@ -595,7 +620,7 @@ namespace Microsoft.AspNetCore.Mvc.Testing
 
             protected override IHost CreateHost(IHostBuilder builder) => _createHost(builder);
 
-            protected override IWebHostBuilder CreateWebHostBuilder() => _createWebHostBuilder();
+            protected override IWebHostBuilder? CreateWebHostBuilder() => _createWebHostBuilder();
 
             protected override IHostBuilder? CreateHostBuilder() => _createHostBuilder();
 

+ 2 - 1
src/Mvc/Mvc.slnf

@@ -22,10 +22,10 @@
       "src\\Html.Abstractions\\src\\Microsoft.AspNetCore.Html.Abstractions.csproj",
       "src\\Http\\Authentication.Abstractions\\src\\Microsoft.AspNetCore.Authentication.Abstractions.csproj",
       "src\\Http\\Authentication.Core\\src\\Microsoft.AspNetCore.Authentication.Core.csproj",
+      "src\\Http\\Features\\src\\Microsoft.Extensions.Features.csproj",
       "src\\Http\\Headers\\src\\Microsoft.Net.Http.Headers.csproj",
       "src\\Http\\Http.Abstractions\\src\\Microsoft.AspNetCore.Http.Abstractions.csproj",
       "src\\Http\\Http.Extensions\\src\\Microsoft.AspNetCore.Http.Extensions.csproj",
-      "src\\Http\\Features\\src\\Microsoft.Extensions.Features.csproj",
       "src\\Http\\Http.Features\\src\\Microsoft.AspNetCore.Http.Features.csproj",
       "src\\Http\\Http\\src\\Microsoft.AspNetCore.Http.csproj",
       "src\\Http\\Metadata\\src\\Microsoft.AspNetCore.Metadata.csproj",
@@ -108,6 +108,7 @@
       "src\\Mvc\\test\\WebSites\\RazorWebSite\\RazorWebSite.csproj",
       "src\\Mvc\\test\\WebSites\\RoutingWebSite\\Mvc.RoutingWebSite.csproj",
       "src\\Mvc\\test\\WebSites\\SecurityWebSite\\SecurityWebSite.csproj",
+      "src\\Mvc\\test\\WebSites\\SimpleWebSiteWithWebApplicationBuilder\\SimpleWebSiteWithWebApplicationBuilder.csproj",
       "src\\Mvc\\test\\WebSites\\SimpleWebSite\\SimpleWebSite.csproj",
       "src\\Mvc\\test\\WebSites\\TagHelpersWebSite\\TagHelpersWebSite.csproj",
       "src\\Mvc\\test\\WebSites\\VersioningWebSite\\VersioningWebSite.csproj",

+ 1 - 0
src/Mvc/test/Mvc.FunctionalTests/Microsoft.AspNetCore.Mvc.FunctionalTests.csproj

@@ -37,6 +37,7 @@
     <ProjectReference Include="..\WebSites\RoutingWebSite\Mvc.RoutingWebSite.csproj" />
     <ProjectReference Include="..\WebSites\SecurityWebSite\SecurityWebSite.csproj" />
     <ProjectReference Include="..\WebSites\SimpleWebSite\SimpleWebSite.csproj" />
+    <ProjectReference Include="..\WebSites\SimpleWebSiteWithWebApplicationBuilder\SimpleWebSiteWithWebApplicationBuilder.csproj" />
     <ProjectReference Include="..\WebSites\TagHelpersWebSite\TagHelpersWebSite.csproj" />
     <ProjectReference Include="..\WebSites\VersioningWebSite\VersioningWebSite.csproj" />
     <ProjectReference Include="..\WebSites\XmlFormattersWebSite\XmlFormattersWebSite.csproj" />

+ 34 - 0
src/Mvc/test/Mvc.FunctionalTests/SimpleWithWebApplicationBuilderTests.cs

@@ -0,0 +1,34 @@
+// 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.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Mvc.FunctionalTests
+{
+    public class SimpleWithWebApplicationBuilderTests : IClassFixture<MvcTestFixture<SimpleWebSiteWithWebApplicationBuilder.FakeStartup>>
+    {
+        public SimpleWithWebApplicationBuilderTests(MvcTestFixture<SimpleWebSiteWithWebApplicationBuilder.FakeStartup> fixture)
+        {
+            Client = fixture.CreateDefaultClient();
+        }
+
+        public HttpClient Client { get; }
+
+        [Fact]
+        public async Task HelloWorld()
+        {
+            // Arrange
+            var expected = "Hello World";
+
+            // Act
+            var content = await Client.GetStringAsync("http://localhost/");
+
+            // Assert
+            Assert.Equal(expected, content);
+        }
+    }
+}

+ 14 - 0
src/Mvc/test/WebSites/SimpleWebSiteWithWebApplicationBuilder/FakeEntryPoint.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+/// <summary>
+/// This is a class we use to reference this assembly statically from tests
+/// </summary>
+namespace SimpleWebSiteWithWebApplicationBuilder
+{
+    public class FakeStartup
+    {
+    }
+}

+ 11 - 0
src/Mvc/test/WebSites/SimpleWebSiteWithWebApplicationBuilder/Program.cs

@@ -0,0 +1,11 @@
+// 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.Builder;
+
+var app = WebApplication.Create(args);
+
+app.MapGet("/", (Func<string>)(() => "Hello World"));
+
+app.Run();

+ 27 - 0
src/Mvc/test/WebSites/SimpleWebSiteWithWebApplicationBuilder/Properties/launchSettings.json

@@ -0,0 +1,27 @@
+{
+  "iisSettings": {
+    "windowsAuthentication": false,
+    "anonymousAuthentication": true,
+    "iisExpress": {
+      "applicationUrl": "http://localhost:51807/",
+      "sslPort": 44365
+    }
+  },
+  "profiles": {
+    "SimpleWebSite": {
+      "commandName": "Project",
+      "launchBrowser": true,
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      },
+      "applicationUrl": "https://localhost:5001;http://localhost:5000"
+    },
+    "IIS Express": {
+      "commandName": "IISExpress",
+      "launchBrowser": true,
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    }
+  }
+}

+ 10 - 0
src/Mvc/test/WebSites/SimpleWebSiteWithWebApplicationBuilder/SimpleWebSiteWithWebApplicationBuilder.csproj

@@ -0,0 +1,10 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+  <PropertyGroup>
+    <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Reference Include="Microsoft.AspNetCore" />
+  </ItemGroup>
+</Project>

+ 4 - 0
src/Mvc/test/WebSites/SimpleWebSiteWithWebApplicationBuilder/readme.md

@@ -0,0 +1,4 @@
+SimpleWebSiteWithWebApplicationBuilder
+===
+This sample web project illustrates a minimal site using WebApplicationBuilder.
+Please build from root (`.\build.cmd` on Windows; `./build.sh` elsewhere) before using this site.