Add IHostApplicationBuilder interface (#86974)
authorEric Erhardt <eric.erhardt@microsoft.com>
Thu, 8 Jun 2023 19:25:49 +0000 (14:25 -0500)
committerGitHub <noreply@github.com>
Thu, 8 Jun 2023 19:25:49 +0000 (14:25 -0500)
* 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

19 files changed:
src/libraries/Microsoft.Extensions.Configuration.Abstractions/ref/Microsoft.Extensions.Configuration.Abstractions.cs
src/libraries/Microsoft.Extensions.Configuration.Abstractions/src/IConfigurationManager.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Configuration/ref/Microsoft.Extensions.Configuration.cs
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationManager.cs
src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.cs
src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostApplicationBuilder.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/Microsoft.Extensions.Hosting.Abstractions.csproj
src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs
src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs
src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/IHostApplicationBuilderTests.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs
src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.csproj
src/libraries/Microsoft.Extensions.Logging.Abstractions/src/ILoggingBuilder.cs [moved from src/libraries/Microsoft.Extensions.Logging/src/ILoggingBuilder.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Logging.Abstractions/src/Microsoft.Extensions.Logging.Abstractions.csproj
src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.Forwards.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.cs
src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.csproj
src/libraries/Microsoft.Extensions.Logging/src/Properties/TypeForwards.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerBuilderExtensionsTests.cs

index 9123a25..03f00e5 100644 (file)
@@ -6,12 +6,22 @@
 
 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; }
     }
@@ -33,15 +43,7 @@ namespace Microsoft.Extensions.Configuration
     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
     {
@@ -57,6 +59,9 @@ namespace Microsoft.Extensions.Configuration
         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);
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Abstractions/src/IConfigurationManager.cs b/src/libraries/Microsoft.Extensions.Configuration.Abstractions/src/IConfigurationManager.cs
new file mode 100644 (file)
index 0000000..84293d4
--- /dev/null
@@ -0,0 +1,15 @@
+// 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
+{
+}
index b5c18ee..63ba609 100644 (file)
@@ -44,7 +44,7 @@ namespace Microsoft.Extensions.Configuration
         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 { } }
index 9bc08c6..19bde16 100644 (file)
@@ -14,12 +14,15 @@ using Microsoft.Extensions.Primitives;
 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;
index b4d4936..88f37fc 100644 (file)
@@ -97,6 +97,15 @@ namespace Microsoft.Extensions.Hosting
         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; }
diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostApplicationBuilder.cs b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostApplicationBuilder.cs
new file mode 100644 (file)
index 0000000..5ae02d0
--- /dev/null
@@ -0,0 +1,65 @@
+// 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;
+}
index 598d4bd..f7881ef 100644 (file)
@@ -22,6 +22,7 @@
     <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'))">
index d8820aa..4e7804b 100644 (file)
@@ -32,7 +32,7 @@ namespace Microsoft.Extensions.Hosting
         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) { }
@@ -40,6 +40,8 @@ namespace Microsoft.Extensions.Hosting
         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 { }
index cd635f9..2de3117 100644 (file)
@@ -14,9 +14,9 @@ using Microsoft.Extensions.Logging;
 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();
@@ -182,45 +182,28 @@ namespace Microsoft.Extensions.Hosting
             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 = () =>
diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/IHostApplicationBuilderTests.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/IHostApplicationBuilderTests.cs
new file mode 100644 (file)
index 0000000..5eeb3eb
--- /dev/null
@@ -0,0 +1,69 @@
+// 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!");
+    }
+}
index 72e6ae6..832ec52 100644 (file)
@@ -44,6 +44,10 @@ namespace Microsoft.Extensions.Logging
     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);
index 0b3042f..90242db 100644 (file)
@@ -6,4 +6,8 @@
   <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>
index afd6970..98722fe 100644 (file)
@@ -37,6 +37,8 @@ Microsoft.Extensions.Logging.Abstractions.NullLogger</PackageDescription>
   </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"
diff --git a/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.Forwards.cs b/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.Forwards.cs
new file mode 100644 (file)
index 0000000..326ba6e
--- /dev/null
@@ -0,0 +1,7 @@
+// 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))]
index 5b7fade..966ed89 100644 (file)
@@ -47,10 +47,6 @@ namespace Microsoft.Extensions.Logging
         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() { }
index 9ded83f..a7d3bfa 100644 (file)
@@ -5,6 +5,7 @@
 
   <ItemGroup>
     <Compile Include="Microsoft.Extensions.Logging.cs" />
+    <Compile Include="Microsoft.Extensions.Logging.Forwards.cs" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/libraries/Microsoft.Extensions.Logging/src/Properties/TypeForwards.cs b/src/libraries/Microsoft.Extensions.Logging/src/Properties/TypeForwards.cs
new file mode 100644 (file)
index 0000000..a075b0e
--- /dev/null
@@ -0,0 +1,4 @@
+// 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))]
index dee690e..70983e3 100644 (file)
@@ -1,6 +1,7 @@
 // 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;
@@ -41,5 +42,16 @@ namespace Microsoft.Extensions.Logging.Test
 
             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);
+        }
     }
 }