From 10b05a3a20312de7acb6136139096fa81890ac49 Mon Sep 17 00:00:00 2001 From: Eduardo Caceres Date: Tue, 30 Sep 2025 16:21:16 +0200 Subject: [PATCH 1/2] Add support for HostApplicationBuilder in AmbientMetadata extension --- ...pplicationMetadataHostBuilderExtensions.cs | 21 +++++++++ ...xtensions.AmbientMetadata.Application.json | 6 ++- .../README.md | 1 + .../AcceptanceTests.cs | 43 +++++++++++++++++++ .../ApplicationMetadataExtensionsTests.cs | 21 +++++++++ 5 files changed, 91 insertions(+), 1 deletion(-) diff --git a/src/Libraries/Microsoft.Extensions.AmbientMetadata.Application/ApplicationMetadataHostBuilderExtensions.cs b/src/Libraries/Microsoft.Extensions.AmbientMetadata.Application/ApplicationMetadataHostBuilderExtensions.cs index e2635bbcc4d..2c7054c4b11 100644 --- a/src/Libraries/Microsoft.Extensions.AmbientMetadata.Application/ApplicationMetadataHostBuilderExtensions.cs +++ b/src/Libraries/Microsoft.Extensions.AmbientMetadata.Application/ApplicationMetadataHostBuilderExtensions.cs @@ -32,4 +32,25 @@ public static IHostBuilder UseApplicationMetadata(this IHostBuilder builder, str .ConfigureAppConfiguration((hostBuilderContext, configurationBuilder) => configurationBuilder.AddApplicationMetadata(hostBuilderContext.HostingEnvironment, sectionName)) .ConfigureServices((hostBuilderContext, serviceCollection) => serviceCollection.AddApplicationMetadata(hostBuilderContext.Configuration.GetSection(sectionName))); } + + /// + /// Registers a configuration provider for application metadata and binds a model object onto the configuration. + /// + /// . + /// The host builder. + /// Section name to bind configuration from. Default set to "ambientmetadata:application". + /// The value of . + /// is . + /// is either , empty, or whitespace. + public static TBuilder UseApplicationMetadata(this TBuilder builder, string sectionName = DefaultSectionName) + where TBuilder : IHostApplicationBuilder + { + _ = Throw.IfNull(builder); + _ = Throw.IfNullOrWhitespace(sectionName); + + _ = builder.Configuration.AddApplicationMetadata(builder.Environment, sectionName); + _ = builder.Services.AddApplicationMetadata(builder.Configuration.GetSection(sectionName)); + + return builder; + } } diff --git a/src/Libraries/Microsoft.Extensions.AmbientMetadata.Application/Microsoft.Extensions.AmbientMetadata.Application.json b/src/Libraries/Microsoft.Extensions.AmbientMetadata.Application/Microsoft.Extensions.AmbientMetadata.Application.json index 35db568194a..ecdcf8916fa 100644 --- a/src/Libraries/Microsoft.Extensions.AmbientMetadata.Application/Microsoft.Extensions.AmbientMetadata.Application.json +++ b/src/Libraries/Microsoft.Extensions.AmbientMetadata.Application/Microsoft.Extensions.AmbientMetadata.Application.json @@ -1,5 +1,5 @@ { - "Name": "Microsoft.Extensions.AmbientMetadata.Application, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "Name": "Microsoft.Extensions.AmbientMetadata.Application, Version=9.10.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "Types": [ { "Type": "class Microsoft.Extensions.AmbientMetadata.ApplicationMetadata", @@ -46,6 +46,10 @@ { "Member": "static Microsoft.Extensions.Hosting.IHostBuilder Microsoft.Extensions.Hosting.ApplicationMetadataHostBuilderExtensions.UseApplicationMetadata(this Microsoft.Extensions.Hosting.IHostBuilder builder, string sectionName = \"ambientmetadata:application\");", "Stage": "Stable" + }, + { + "Member": "static TBuilder Microsoft.Extensions.Hosting.ApplicationMetadataHostBuilderExtensions.UseApplicationMetadata(this TBuilder builder, string sectionName = \"ambientmetadata:application\");", + "Stage": "Stable" } ] }, diff --git a/src/Libraries/Microsoft.Extensions.AmbientMetadata.Application/README.md b/src/Libraries/Microsoft.Extensions.AmbientMetadata.Application/README.md index 45cba75f480..5ef2e71c271 100644 --- a/src/Libraries/Microsoft.Extensions.AmbientMetadata.Application/README.md +++ b/src/Libraries/Microsoft.Extensions.AmbientMetadata.Application/README.md @@ -26,6 +26,7 @@ The services can be registered using any of the following methods: ```csharp public static IHostBuilder UseApplicationMetadata(this IHostBuilder builder, string sectionName = DefaultSectionName) +public static TBuilder UseApplicationMetadata(this TBuilder builder, string sectionName = DefaultSectionName) where TBuilder : IHostApplicationBuilder public static IServiceCollection AddApplicationMetadata(this IServiceCollection services, Action configure) ``` diff --git a/test/Libraries/Microsoft.Extensions.AmbientMetadata.Application.Tests/AcceptanceTests.cs b/test/Libraries/Microsoft.Extensions.AmbientMetadata.Application.Tests/AcceptanceTests.cs index d66842b018b..451dcea1cb2 100644 --- a/test/Libraries/Microsoft.Extensions.AmbientMetadata.Application.Tests/AcceptanceTests.cs +++ b/test/Libraries/Microsoft.Extensions.AmbientMetadata.Application.Tests/AcceptanceTests.cs @@ -39,6 +39,22 @@ await RunAsync( }, sectionName); + [Theory] + [InlineData("ambientmetadata:application")] + [InlineData(null)] + public async Task UseApplicationMetadata_HostApplicationBuilder_CreatesPopulatesAndRegistersOptions(string? sectionName) => + await RunAsync_HostBuilder( + (options, hostEnvironment) => + { + options.BuildVersion.Should().Be(_metadata.BuildVersion); + options.DeploymentRing.Should().Be(_metadata.DeploymentRing); + options.ApplicationName.Should().Be(_metadata.ApplicationName); + options.EnvironmentName.Should().Be(hostEnvironment.EnvironmentName); + + return Task.CompletedTask; + }, + sectionName); + private static async Task RunAsync(Func func, string? sectionName) { using var host = await FakeHost.CreateBuilder() @@ -60,4 +76,31 @@ await func(host.Services.GetRequiredService>().Val host.Services.GetRequiredService()); await host.StopAsync(); } + + private static async Task RunAsync_HostBuilder(Func func, string? sectionName) + { + var builder = Host.CreateEmptyApplicationBuilder(new() + { + ApplicationName = _metadata.ApplicationName + }); + + // need to set applicationName manually, because + // netfx console test runner cannot get assebly name + // to be able to set it automatically + // see https://source.dot.net/#Microsoft.Extensions.Hosting/HostBuilder.cs,240 + builder + .UseApplicationMetadata(sectionName ?? "ambientmetadata:application") + .Services.AddApplicationMetadata(metadata => + { + metadata.BuildVersion = _metadata.BuildVersion; + metadata.DeploymentRing = _metadata.DeploymentRing; + }); + + using var host = builder.Build(); + await host.StartAsync(); + + await func(host.Services.GetRequiredService>().Value, + host.Services.GetRequiredService()); + await host.StopAsync(); + } } diff --git a/test/Libraries/Microsoft.Extensions.AmbientMetadata.Application.Tests/ApplicationMetadataExtensionsTests.cs b/test/Libraries/Microsoft.Extensions.AmbientMetadata.Application.Tests/ApplicationMetadataExtensionsTests.cs index 5091bd87e21..7fbc66a1e73 100644 --- a/test/Libraries/Microsoft.Extensions.AmbientMetadata.Application.Tests/ApplicationMetadataExtensionsTests.cs +++ b/test/Libraries/Microsoft.Extensions.AmbientMetadata.Application.Tests/ApplicationMetadataExtensionsTests.cs @@ -40,10 +40,20 @@ public void ApplicationMetadataExtensions_GivenAnyNullArgument_Throws() Assert.Throws(() => serviceCollection.AddApplicationMetadata((Action)null!)); Assert.Throws(() => serviceCollection.AddApplicationMetadata((IConfigurationSection)null!)); Assert.Throws(() => ((IHostBuilder)null!).UseApplicationMetadata(_fixture.Create())); + Assert.Throws(() => ((IHostApplicationBuilder)null!).UseApplicationMetadata(_fixture.Create())); Assert.Throws(() => new ConfigurationBuilder().AddApplicationMetadata(null!)); Assert.Throws(() => ((IConfigurationBuilder)null!).AddApplicationMetadata(null!)); } + [Fact] + public void ApplicationMetadataExtensions_GivenEmptyAction_DoesNotThrow() + { + var serviceCollection = new ServiceCollection(); + var config = new ConfigurationBuilder().Build(); + + Assert.Null(Record.Exception(() => serviceCollection.AddApplicationMetadata(_ => { }))); + } + [Theory] [InlineData(null)] [InlineData("")] @@ -66,6 +76,17 @@ public void UseApplicationMetadata_InvalidSectionName_Throws(string? sectionName act.Should().Throw(); } + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + [InlineData(" ")] + public void UseApplicationMetadata_HostApplicationBuilder_InvalidSectionName_Throws(string? sectionName) + { + var act = () => Host.CreateEmptyApplicationBuilder(new()).UseApplicationMetadata(sectionName!); + act.Should().Throw(); + } + [Fact] public void AddApplicationMetadata_BuildsConfig() { From fb80619d6684ab3b1c9d335cdba27593ea223d24 Mon Sep 17 00:00:00 2001 From: Eduardo Caceres Date: Tue, 30 Sep 2025 16:59:54 +0200 Subject: [PATCH 2/2] Fix typos --- .../AcceptanceTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Libraries/Microsoft.Extensions.AmbientMetadata.Application.Tests/AcceptanceTests.cs b/test/Libraries/Microsoft.Extensions.AmbientMetadata.Application.Tests/AcceptanceTests.cs index 451dcea1cb2..ace2a6148e6 100644 --- a/test/Libraries/Microsoft.Extensions.AmbientMetadata.Application.Tests/AcceptanceTests.cs +++ b/test/Libraries/Microsoft.Extensions.AmbientMetadata.Application.Tests/AcceptanceTests.cs @@ -60,7 +60,7 @@ private static async Task RunAsync(Func