* Add IHostApplicationBuilder and implement it in HostApplicationBuilder.
- Move ILoggingBuilder from MS.Ext.Logging to MS.Ext.Logging.Abstractions so IHostApplicationBuilder can reference it.
- Add IConfigurationManager and implement it in ConfigurationManager.
- Add CompatiibilitySuppressions. These errors are caused by ILoggingBuilder being moved to Logging.Abstractions, and ApiCompat not respecting TypeForwardedTo attributes.
Fix #85486
namespace Microsoft.Extensions.Configuration
{
+ public readonly partial struct ConfigurationDebugViewContext
+ {
+ private readonly object _dummy;
+ private readonly int _dummyPrimitive;
+ public ConfigurationDebugViewContext(string path, string key, string? value, Microsoft.Extensions.Configuration.IConfigurationProvider configurationProvider) { throw null; }
+ public Microsoft.Extensions.Configuration.IConfigurationProvider ConfigurationProvider { get { throw null; } }
+ public string Key { get { throw null; } }
+ public string Path { get { throw null; } }
+ public string? Value { get { throw null; } }
+ }
public static partial class ConfigurationExtensions
{
public static Microsoft.Extensions.Configuration.IConfigurationBuilder Add<TSource>(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, System.Action<TSource>? configureSource) where TSource : Microsoft.Extensions.Configuration.IConfigurationSource, new() { throw null; }
public static System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, string?>> AsEnumerable(this Microsoft.Extensions.Configuration.IConfiguration configuration) { throw null; }
public static System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, string?>> AsEnumerable(this Microsoft.Extensions.Configuration.IConfiguration configuration, bool makePathsRelative) { throw null; }
- public static bool Exists([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] this Microsoft.Extensions.Configuration.IConfigurationSection? section) { throw null; }
+ public static bool Exists([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] this Microsoft.Extensions.Configuration.IConfigurationSection? section) { throw null; }
public static string? GetConnectionString(this Microsoft.Extensions.Configuration.IConfiguration configuration, string name) { throw null; }
public static Microsoft.Extensions.Configuration.IConfigurationSection GetRequiredSection(this Microsoft.Extensions.Configuration.IConfiguration configuration, string key) { throw null; }
}
public static partial class ConfigurationRootExtensions
{
public static string GetDebugView(this Microsoft.Extensions.Configuration.IConfigurationRoot root) { throw null; }
- public static string GetDebugView(this IConfigurationRoot root, System.Func<ConfigurationDebugViewContext, string>? processValue) { throw null; }
- }
- public readonly partial struct ConfigurationDebugViewContext
- {
- public ConfigurationDebugViewContext(string path, string key, string? value, IConfigurationProvider configurationProvider) { throw null; }
- public string Path { get; }
- public string Key { get; }
- public string? Value { get; }
- public IConfigurationProvider ConfigurationProvider { get; }
+ public static string GetDebugView(this Microsoft.Extensions.Configuration.IConfigurationRoot root, System.Func<Microsoft.Extensions.Configuration.ConfigurationDebugViewContext, string>? processValue) { throw null; }
}
public partial interface IConfiguration
{
Microsoft.Extensions.Configuration.IConfigurationBuilder Add(Microsoft.Extensions.Configuration.IConfigurationSource source);
Microsoft.Extensions.Configuration.IConfigurationRoot Build();
}
+ public partial interface IConfigurationManager : Microsoft.Extensions.Configuration.IConfiguration, Microsoft.Extensions.Configuration.IConfigurationBuilder
+ {
+ }
public partial interface IConfigurationProvider
{
System.Collections.Generic.IEnumerable<string> GetChildKeys(System.Collections.Generic.IEnumerable<string> earlierKeys, string? parentPath);
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.Extensions.Configuration;
+
+/// <summary>
+/// Represents a mutable configuration object.
+/// </summary>
+/// <remarks>
+/// It is both an <see cref="IConfigurationBuilder"/> and an <see cref="IConfiguration"/>.
+/// As sources are added, it updates its current view of configuration.
+/// </remarks>
+public interface IConfigurationManager : IConfiguration, IConfigurationBuilder
+{
+}
public static Microsoft.Extensions.Configuration.ConfigurationKeyComparer Instance { get { throw null; } }
public int Compare(string? x, string? y) { throw null; }
}
- public sealed partial class ConfigurationManager : Microsoft.Extensions.Configuration.IConfiguration, Microsoft.Extensions.Configuration.IConfigurationBuilder, Microsoft.Extensions.Configuration.IConfigurationRoot, System.IDisposable
+ public sealed partial class ConfigurationManager : Microsoft.Extensions.Configuration.IConfiguration, Microsoft.Extensions.Configuration.IConfigurationBuilder, Microsoft.Extensions.Configuration.IConfigurationManager, Microsoft.Extensions.Configuration.IConfigurationRoot, System.IDisposable
{
public ConfigurationManager() { }
public string? this[string key] { get { throw null; } set { } }
namespace Microsoft.Extensions.Configuration
{
/// <summary>
- /// ConfigurationManager is a mutable configuration object. It is both an <see cref="IConfigurationBuilder"/> and an <see cref="IConfigurationRoot"/>.
- /// As sources are added, it updates its current view of configuration.
+ /// Represents a mutable configuration object.
/// </summary>
+ /// <remarks>
+ /// It is both an <see cref="IConfigurationBuilder"/> and an <see cref="IConfigurationRoot"/>.
+ /// As sources are added, it updates its current view of configuration.
+ /// </remarks>
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(ConfigurationManagerDebugView))]
- public sealed class ConfigurationManager : IConfigurationBuilder, IConfigurationRoot, IDisposable
+ public sealed class ConfigurationManager : IConfigurationManager, IConfigurationRoot, IDisposable
{
// Concurrently modifying config sources or properties is not thread-safe. However, it is thread-safe to read config while modifying sources or properties.
private readonly ConfigurationSources _sources;
System.Threading.Tasks.Task StartAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
System.Threading.Tasks.Task StopAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
}
+ public partial interface IHostApplicationBuilder
+ {
+ Microsoft.Extensions.Configuration.IConfigurationManager Configuration { get; }
+ Microsoft.Extensions.Hosting.IHostEnvironment Environment { get; }
+ Microsoft.Extensions.Logging.ILoggingBuilder Logging { get; }
+ System.Collections.Generic.IDictionary<object, object> Properties { get; }
+ Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get; }
+ void ConfigureContainer<TContainerBuilder>(Microsoft.Extensions.DependencyInjection.IServiceProviderFactory<TContainerBuilder> factory, System.Action<TContainerBuilder>? configure = null) where TContainerBuilder : notnull;
+ }
public partial interface IHostApplicationLifetime
{
System.Threading.CancellationToken ApplicationStarted { get; }
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace Microsoft.Extensions.Hosting;
+
+/// <summary>
+/// Represents a hosted applications and services builder which helps manage configuration, logging, lifetime, and more.
+/// </summary>
+public interface IHostApplicationBuilder
+{
+ /// <summary>
+ /// Gets a central location for sharing state between components during the host building process.
+ /// </summary>
+ IDictionary<object, object> Properties { get; }
+
+ /// <summary>
+ /// Gets the set of key/value configuration properties.
+ /// </summary>
+ /// <remarks>
+ /// This can be mutated by adding more configuration sources, which will update its current view.
+ /// </remarks>
+ IConfigurationManager Configuration { get; }
+
+ /// <summary>
+ /// Gets the information about the hosting environment an application is running in.
+ /// </summary>
+ IHostEnvironment Environment { get; }
+
+ /// <summary>
+ /// Gets a collection of logging providers for the application to compose. This is useful for adding new logging providers.
+ /// </summary>
+ ILoggingBuilder Logging { get; }
+
+ /// <summary>
+ /// Gets a collection of services for the application to compose. This is useful for adding user provided or framework provided services.
+ /// </summary>
+ IServiceCollection Services { get; }
+
+ /// <summary>
+ /// Registers a <see cref="IServiceProviderFactory{TContainerBuilder}" /> instance to be used to create the <see cref="IServiceProvider" />.
+ /// </summary>
+ /// <param name="factory">The factory object that can create the <typeparamref name="TContainerBuilder"/> and <see cref="IServiceProvider"/>.</param>
+ /// <param name="configure">
+ /// A delegate used to configure the <typeparamref T="TContainerBuilder" />. This can be used to configure services using
+ /// APIS specific to the <see cref="IServiceProviderFactory{TContainerBuilder}" /> implementation.
+ /// </param>
+ /// <typeparam name="TContainerBuilder">The type of builder provided by the <see cref="IServiceProviderFactory{TContainerBuilder}" />.</typeparam>
+ /// <remarks>
+ /// <para>
+ /// The <see cref="IServiceProvider"/> is created when this builder is built and so the delegate provided
+ /// by <paramref name="configure"/> will run after all other services have been registered.
+ /// </para>
+ /// <para>
+ /// Multiple calls to <see cref="ConfigureContainer{TContainerBuilder}(IServiceProviderFactory{TContainerBuilder}, Action{TContainerBuilder})"/> will replace
+ /// the previously stored <paramref name="factory"/> and <paramref name="configure"/> delegate.
+ /// </para>
+ /// </remarks>
+ void ConfigureContainer<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory, Action<TContainerBuilder>? configure = null) where TContainerBuilder : notnull;
+}
<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.Configuration.Abstractions\src\Microsoft.Extensions.Configuration.Abstractions.csproj" />
<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.DependencyInjection.Abstractions\src\Microsoft.Extensions.DependencyInjection.Abstractions.csproj" />
<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.FileProviders.Abstractions\src\Microsoft.Extensions.FileProviders.Abstractions.csproj" />
+ <ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.Logging.Abstractions\src\Microsoft.Extensions.Logging.Abstractions.csproj" />
</ItemGroup>
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'netstandard2.1'))">
public static Microsoft.Extensions.Hosting.IHostBuilder CreateDefaultBuilder(string[]? args) { throw null; }
public static Microsoft.Extensions.Hosting.HostApplicationBuilder CreateEmptyApplicationBuilder(Microsoft.Extensions.Hosting.HostApplicationBuilderSettings? settings) { throw null; }
}
- public sealed partial class HostApplicationBuilder
+ public sealed partial class HostApplicationBuilder : Microsoft.Extensions.Hosting.IHostApplicationBuilder
{
public HostApplicationBuilder() { }
public HostApplicationBuilder(Microsoft.Extensions.Hosting.HostApplicationBuilderSettings? settings) { }
public Microsoft.Extensions.Configuration.ConfigurationManager Configuration { get { throw null; } }
public Microsoft.Extensions.Hosting.IHostEnvironment Environment { get { throw null; } }
public Microsoft.Extensions.Logging.ILoggingBuilder Logging { get { throw null; } }
+ Microsoft.Extensions.Configuration.IConfigurationManager Microsoft.Extensions.Hosting.IHostApplicationBuilder.Configuration { get { throw null; } }
+ System.Collections.Generic.IDictionary<object, object> Microsoft.Extensions.Hosting.IHostApplicationBuilder.Properties { get { throw null; } }
public Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get { throw null; } }
public Microsoft.Extensions.Hosting.IHost Build() { throw null; }
public void ConfigureContainer<TContainerBuilder>(Microsoft.Extensions.DependencyInjection.IServiceProviderFactory<TContainerBuilder> factory, System.Action<TContainerBuilder>? configure = null) where TContainerBuilder : notnull { }
namespace Microsoft.Extensions.Hosting
{
/// <summary>
- /// A builder for hosted applications and services which helps manage configuration, logging, lifetime and more.
+ /// Represents a hosted applications and services builder which helps manage configuration, logging, lifetime, and more.
/// </summary>
- public sealed class HostApplicationBuilder
+ public sealed class HostApplicationBuilder : IHostApplicationBuilder
{
private readonly HostBuilderContext _hostBuilderContext;
private readonly ServiceCollection _serviceCollection = new();
logging = new LoggingBuilder(Services);
}
- /// <summary>
- /// Provides information about the hosting environment an application is running in.
- /// </summary>
+ IDictionary<object, object> IHostApplicationBuilder.Properties => _hostBuilderContext.Properties;
+
+ /// <inheritdoc />
public IHostEnvironment Environment => _environment;
/// <summary>
- /// A collection of services for the application to compose. This is useful for adding user provided or framework provided services.
+ /// Gets the set of key/value configuration properties.
/// </summary>
+ /// <remarks>
+ /// This can be mutated by adding more configuration sources, which will update its current view.
+ /// </remarks>
public ConfigurationManager Configuration { get; }
- /// <summary>
- /// A collection of services for the application to compose. This is useful for adding user provided or framework provided services.
- /// </summary>
+ IConfigurationManager IHostApplicationBuilder.Configuration => Configuration;
+
+ /// <inheritdoc />
public IServiceCollection Services => _serviceCollection;
- /// <summary>
- /// A collection of logging providers for the application to compose. This is useful for adding new logging providers.
- /// </summary>
+ /// <inheritdoc />
public ILoggingBuilder Logging => _logging;
- /// <summary>
- /// Registers a <see cref="IServiceProviderFactory{TContainerBuilder}" /> instance to be used to create the <see cref="IServiceProvider" />.
- /// </summary>
- /// <param name="factory">The <see cref="IServiceProviderFactory{TContainerBuilder}" />.</param>
- /// <param name="configure">
- /// A delegate used to configure the <typeparamref T="TContainerBuilder" />. This can be used to configure services using
- /// APIS specific to the <see cref="IServiceProviderFactory{TContainerBuilder}" /> implementation.
- /// </param>
- /// <typeparam name="TContainerBuilder">The type of builder provided by the <see cref="IServiceProviderFactory{TContainerBuilder}" />.</typeparam>
- /// <remarks>
- /// <para>
- /// <see cref="ConfigureContainer{TContainerBuilder}(IServiceProviderFactory{TContainerBuilder}, Action{TContainerBuilder})"/> is called by <see cref="Build"/>
- /// and so the delegate provided by <paramref name="configure"/> will run after all other services have been registered.
- /// </para>
- /// <para>
- /// Multiple calls to <see cref="ConfigureContainer{TContainerBuilder}(IServiceProviderFactory{TContainerBuilder}, Action{TContainerBuilder})"/> will replace
- /// the previously stored <paramref name="factory"/> and <paramref name="configure"/> delegate.
- /// </para>
- /// </remarks>
+ /// <inheritdoc />
public void ConfigureContainer<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory, Action<TContainerBuilder>? configure = null) where TContainerBuilder : notnull
{
_createServiceProvider = () =>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting.Fakes;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Console;
+using Xunit;
+
+namespace Microsoft.Extensions.Hosting.Tests;
+
+public class IHostApplicationBuilderTests
+{
+ [Fact]
+ public void TestIHostApplicationBuilderCanBeUsedInExtensionMethod()
+ {
+ HostApplicationBuilder builder = Host.CreateEmptyApplicationBuilder(new HostApplicationBuilderSettings
+ {
+ EnvironmentName = "Development"
+ });
+
+ builder.VerifyBuilderWorks();
+
+ using IHost host = builder.Build();
+
+ // VerifyBuilderWorks should have configured a FakeServiceProviderFactory with the following State.
+ FakeServiceCollection fakeServices = host.Services.GetRequiredService<FakeServiceCollection>();
+ Assert.Equal("Hi!", fakeServices.State);
+ }
+}
+
+internal static class HostBuilderExtensions
+{
+ public static void VerifyBuilderWorks(this IHostApplicationBuilder builder)
+ {
+ var propertyKey = typeof(HostBuilderExtensions);
+ builder.Properties[propertyKey] = 3;
+ Assert.Equal(3, builder.Properties[propertyKey]);
+
+ Assert.Equal(1, builder.Configuration.GetChildren().Count());
+ Assert.Equal(2, builder.Configuration.Sources.Count); // there's an empty source by default
+ Assert.Equal("Development", builder.Configuration[HostDefaults.EnvironmentKey]);
+
+ builder.Configuration.AddInMemoryCollection(new Dictionary<string, string>
+ {
+ ["Key1"] = "value1"
+ });
+
+ Assert.Equal(2, builder.Configuration.GetChildren().Count());
+ Assert.Equal(3, builder.Configuration.Sources.Count);
+ Assert.Equal("value1", builder.Configuration["Key1"]);
+ Assert.Null(builder.Configuration["Key2"]);
+
+ Assert.True(builder.Environment.IsDevelopment());
+ Assert.NotNull(builder.Environment.ContentRootFileProvider);
+
+ Assert.DoesNotContain(builder.Services, sd => sd.ImplementationType == typeof(ConsoleLoggerProvider));
+ builder.Logging.AddConsole();
+ Assert.Contains(builder.Services, sd => sd.ImplementationType == typeof(ConsoleLoggerProvider));
+
+ builder.Services.AddSingleton(typeof(IHostApplicationBuilderTests));
+
+ builder.ConfigureContainer(new FakeServiceProviderFactory(), container => container.State = "Hi!");
+ }
+}
public partial interface ILogger<out TCategoryName> : Microsoft.Extensions.Logging.ILogger
{
}
+ public interface ILoggingBuilder
+ {
+ Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get; }
+ }
public partial interface ISupportExternalScope
{
void SetScopeProvider(Microsoft.Extensions.Logging.IExternalScopeProvider scopeProvider);
<ItemGroup>
<Compile Include="Microsoft.Extensions.Logging.Abstractions.cs" />
</ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.DependencyInjection.Abstractions\ref\Microsoft.Extensions.DependencyInjection.Abstractions.csproj" />
+ </ItemGroup>
</Project>
</ItemGroup>
<ItemGroup>
+ <ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.DependencyInjection.Abstractions\src\Microsoft.Extensions.DependencyInjection.Abstractions.csproj" />
+
<ProjectReference Include="..\gen\Microsoft.Extensions.Logging.Generators.Roslyn3.11.csproj"
ReferenceOutputAssembly="false"
PackAsAnalyzer="true"
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ------------------------------------------------------------------------------
+// Changes to this file must follow the https://aka.ms/api-review process.
+// ------------------------------------------------------------------------------
+
+[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.Extensions.Logging.ILoggingBuilder))]
public static Microsoft.Extensions.Logging.LoggerFilterOptions AddFilter<T>(this Microsoft.Extensions.Logging.LoggerFilterOptions builder, string? category, System.Func<Microsoft.Extensions.Logging.LogLevel, bool> levelFilter) where T : Microsoft.Extensions.Logging.ILoggerProvider { throw null; }
public static Microsoft.Extensions.Logging.LoggerFilterOptions AddFilter<T>(this Microsoft.Extensions.Logging.LoggerFilterOptions builder, string? category, Microsoft.Extensions.Logging.LogLevel level) where T : Microsoft.Extensions.Logging.ILoggerProvider { throw null; }
}
- public partial interface ILoggingBuilder
- {
- Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get; }
- }
public partial class LoggerFactory : Microsoft.Extensions.Logging.ILoggerFactory, System.IDisposable
{
public LoggerFactory() { }
<ItemGroup>
<Compile Include="Microsoft.Extensions.Logging.cs" />
+ <Compile Include="Microsoft.Extensions.Logging.Forwards.cs" />
</ItemGroup>
<ItemGroup>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.Extensions.Logging.ILoggingBuilder))]
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
Assert.True(options.Value.CaptureScopes);
}
+
+ /// <summary>
+ /// Verifies that the TypeForwardedTo attribute is defined correctly by ensuring we can
+ /// reference the ILoggingBuilder type through the Microsoft.Extensions.Logging.dll.
+ /// </summary>
+ [Fact]
+ public void TypeForwardIsCorrect()
+ {
+ Type builderType = Type.GetType("Microsoft.Extensions.Logging.ILoggingBuilder, Microsoft.Extensions.Logging");
+ Assert.Equal(typeof(ILoggingBuilder), builderType);
+ }
}
}