From 41b8fb189909cac5dbf189a5116d2144d0121306 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Thu, 1 Apr 2021 02:19:40 -0700 Subject: [PATCH] Fixed de-duping hosting startup assemblies - De-dupe hosting startup assemblies based on assembly instance not the name of the assembly. Names can be short or long and we don't know if they resolve to the same assembly. Instead of trying to mess with names, we just check to make sure that the instance isn't the same. --- .../src/GenericHost/GenericWebHostBuilder.cs | 10 ++++++++- src/Hosting/Hosting/src/WebHostBuilder.cs | 9 +++++++- .../Hosting/test/WebHostBuilderTests.cs | 21 +++++++++++++++++++ .../TestHostingStartup1.cs | 12 ++++++++++- 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs index 0629eb2bc785..ddb35bf8495a 100644 --- a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs +++ b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs @@ -129,15 +129,23 @@ private void ExecuteHostingStartups() } var exceptions = new List(); + var processed = new HashSet(); + _hostingStartupWebHostBuilder = new HostingStartupWebHostBuilder(this); // Execute the hosting startup assemblies - foreach (var assemblyName in webHostOptions.GetFinalHostingStartupAssemblies().Distinct(StringComparer.OrdinalIgnoreCase)) + foreach (var assemblyName in webHostOptions.GetFinalHostingStartupAssemblies()) { try { var assembly = Assembly.Load(new AssemblyName(assemblyName)); + if (!processed.Add(assembly)) + { + // Already processed, skip it + continue; + } + foreach (var attribute in assembly.GetCustomAttributes()) { var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType)!; diff --git a/src/Hosting/Hosting/src/WebHostBuilder.cs b/src/Hosting/Hosting/src/WebHostBuilder.cs index 0e809be7cf1e..0ae151dd83f6 100644 --- a/src/Hosting/Hosting/src/WebHostBuilder.cs +++ b/src/Hosting/Hosting/src/WebHostBuilder.cs @@ -229,14 +229,21 @@ private IServiceCollection BuildCommonServices(out AggregateException? hostingSt if (!_options.PreventHostingStartup) { var exceptions = new List(); + var processed = new HashSet(); // Execute the hosting startup assemblies - foreach (var assemblyName in _options.GetFinalHostingStartupAssemblies().Distinct(StringComparer.OrdinalIgnoreCase)) + foreach (var assemblyName in _options.GetFinalHostingStartupAssemblies()) { try { var assembly = Assembly.Load(new AssemblyName(assemblyName)); + if (!processed.Add(assembly)) + { + // Already processed, skip it + continue; + } + foreach (var attribute in assembly.GetCustomAttributes()) { var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType)!; diff --git a/src/Hosting/Hosting/test/WebHostBuilderTests.cs b/src/Hosting/Hosting/test/WebHostBuilderTests.cs index 4e55fa9155aa..752b56b1b7a9 100644 --- a/src/Hosting/Hosting/test/WebHostBuilderTests.cs +++ b/src/Hosting/Hosting/test/WebHostBuilderTests.cs @@ -903,6 +903,27 @@ public void Build_RunsHostingStartupAssembliesIfSpecified(IWebHostBuilder builde using (var host = builder.Build()) { Assert.Equal("1", builder.GetSetting("testhostingstartup1")); + Assert.Equal("1", builder.GetSetting("testhostingstartup1_calls")); + } + } + + [Theory] + [MemberData(nameof(DefaultWebHostBuildersWithConfig))] + public void Build_RunsDeduplicatedHostingStartupAssembliesIfSpecified(IWebHostBuilder builder) + { + var fullName = typeof(TestStartupAssembly1.TestHostingStartup1).Assembly.FullName; + var name = typeof(TestStartupAssembly1.TestHostingStartup1).Assembly.GetName().Name; + + builder = builder + .CaptureStartupErrors(false) + .UseSetting(WebHostDefaults.HostingStartupAssembliesKey, fullName + ";" + name) + .Configure(app => { }) + .UseServer(new TestServer()); + + using (var host = builder.Build()) + { + Assert.Equal("1", builder.GetSetting("testhostingstartup1")); + Assert.Equal("1", builder.GetSetting("testhostingstartup1_calls")); } } diff --git a/src/Hosting/test/testassets/TestStartupAssembly1/TestHostingStartup1.cs b/src/Hosting/test/testassets/TestStartupAssembly1/TestHostingStartup1.cs index e8519c83c36c..737825fa9645 100644 --- a/src/Hosting/test/testassets/TestStartupAssembly1/TestHostingStartup1.cs +++ b/src/Hosting/test/testassets/TestStartupAssembly1/TestHostingStartup1.cs @@ -1,6 +1,7 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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.Globalization; using Microsoft.AspNetCore.Hosting; [assembly: HostingStartup(typeof(TestStartupAssembly1.TestHostingStartup1))] @@ -11,8 +12,17 @@ public class TestHostingStartup1 : IHostingStartup { public void Configure(IWebHostBuilder builder) { + var calls = builder.GetSetting("testhostingstartup1_calls"); + var numCalls = 1; + + if (calls != null) + { + numCalls = int.Parse(calls, CultureInfo.InvariantCulture) + 1; + } + builder.UseSetting("testhostingstartup1", "1"); builder.UseSetting("testhostingstartup_chain", builder.GetSetting("testhostingstartup_chain") + "1"); + builder.UseSetting("testhostingstartup1_calls", numCalls.ToString(CultureInfo.InvariantCulture)); } } }