Make Extensions linker friendly. (#40431)
authorEric Erhardt <eric.erhardt@microsoft.com>
Fri, 7 Aug 2020 19:35:53 +0000 (14:35 -0500)
committerGitHub <noreply@github.com>
Fri, 7 Aug 2020 19:35:53 +0000 (14:35 -0500)
* Make Extensions linker friendly.

Annotating the rest of the Microsoft.Extensions libraries.

Fix #40396

* Disable parallelism in trimming tests to fix race condition.

42 files changed:
src/libraries/Common/src/Extensions/ProviderAliasUtilities/ProviderAliasUtilities.cs
src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/Microsoft.Extensions.Hosting.Abstractions.csproj
src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/ServiceCollectionHostedServiceExtensions.cs
src/libraries/Microsoft.Extensions.Hosting/tests/TrimmingTests/AddHostedServiceTests.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Hosting/tests/TrimmingTests/Microsoft.Extensions.Hosting.TrimmingTests.proj [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Http/Microsoft.Extensions.Http.sln
src/libraries/Microsoft.Extensions.Http/src/DefaultTypedHttpClientFactory.cs
src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/HttpClientBuilderExtensions.cs
src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/HttpClientFactoryServiceCollectionExtensions.cs
src/libraries/Microsoft.Extensions.Http/src/ITypedHttpClientFactory.cs
src/libraries/Microsoft.Extensions.Http/src/Microsoft.Extensions.Http.csproj
src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/DefaultHttpClientFactoryTest.cs [moved from src/libraries/Microsoft.Extensions.Http/tests/DefaultHttpClientFactoryTest.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/DefaultHttpMessageHandlerBuilderTest.cs [moved from src/libraries/Microsoft.Extensions.Http/tests/DefaultHttpMessageHandlerBuilderTest.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/DependencyInjection/HttpClientFactoryServiceCollectionExtensionsTest.cs [moved from src/libraries/Microsoft.Extensions.Http/tests/DependencyInjection/HttpClientFactoryServiceCollectionExtensionsTest.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/DependencyInjection/OtherTestOptions.cs [moved from src/libraries/Microsoft.Extensions.Http/tests/DependencyInjection/OtherTestOptions.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/HttpMessageHandlerBuilderTest.cs [moved from src/libraries/Microsoft.Extensions.Http/tests/HttpMessageHandlerBuilderTest.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/ITestTypedClient.cs [moved from src/libraries/Microsoft.Extensions.Http/tests/ITestTypedClient.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/Logging/HttpHeadersLogValueTest.cs [moved from src/libraries/Microsoft.Extensions.Http/tests/Logging/HttpHeadersLogValueTest.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/Logging/RedactedLogValueIntegrationTest.cs [moved from src/libraries/Microsoft.Extensions.Http/tests/Logging/RedactedLogValueIntegrationTest.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/Microsoft.Extensions.Http.Tests.csproj [moved from src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests.csproj with 90% similarity]
src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/TestTypedClient.cs [moved from src/libraries/Microsoft.Extensions.Http/tests/TestTypedClient.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Http/tests/TrimmingTests/AddTypedClientTests.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Http/tests/TrimmingTests/Microsoft.Extensions.Http.TrimmingTests.proj [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging.Console/Microsoft.Extensions.Logging.Console.sln
src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerExtensions.cs
src/libraries/Microsoft.Extensions.Logging.Console/src/FormatterOptionsMonitor.cs
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj
src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/AnsiParserTests.cs [moved from src/libraries/Microsoft.Extensions.Logging.Console/tests/AnsiParserTests.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/Console/ConsoleContext.cs [moved from src/libraries/Microsoft.Extensions.Logging.Console/tests/Console/ConsoleContext.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/Console/ConsoleSink.cs [moved from src/libraries/Microsoft.Extensions.Logging.Console/tests/Console/ConsoleSink.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/Console/TestConsole.cs [moved from src/libraries/Microsoft.Extensions.Logging.Console/tests/Console/TestConsole.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/ConsoleFormatterTests.cs [moved from src/libraries/Microsoft.Extensions.Logging.Console/tests/ConsoleFormatterTests.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/ConsoleLoggerExtensionsTests.cs [moved from src/libraries/Microsoft.Extensions.Logging.Console/tests/ConsoleLoggerExtensionsTests.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/ConsoleLoggerTest.cs [moved from src/libraries/Microsoft.Extensions.Logging.Console/tests/ConsoleLoggerTest.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/JsonConsoleFormatterTests.cs [moved from src/libraries/Microsoft.Extensions.Logging.Console/tests/JsonConsoleFormatterTests.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/Microsoft.Extensions.Logging.Console.Tests.csproj [moved from src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests.csproj with 58% similarity]
src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/SimpleConsoleFormatterTests.cs [moved from src/libraries/Microsoft.Extensions.Logging.Console/tests/SimpleConsoleFormatterTests.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/TestFormatterOptionsMonitor.cs [moved from src/libraries/Microsoft.Extensions.Logging.Console/tests/TestFormatterOptionsMonitor.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/TextWriterExtensionsTests.cs [moved from src/libraries/Microsoft.Extensions.Logging.Console/tests/TextWriterExtensionsTests.cs with 100% similarity]
src/libraries/Microsoft.Extensions.Logging.Console/tests/TrimmingTests/AddConsoleFormatterTests.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Logging.Console/tests/TrimmingTests/Microsoft.Extensions.Logging.Console.TrimmingTests.proj [new file with mode: 0644]
src/libraries/tests.proj

index 4fb6f86..40e80be 100644 (file)
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
+using System.Diagnostics;
 using System.Reflection;
 
 namespace Microsoft.Extensions.Logging
@@ -9,21 +10,18 @@ namespace Microsoft.Extensions.Logging
     internal static class ProviderAliasUtilities
     {
         private const string AliasAttibuteTypeFullName = "Microsoft.Extensions.Logging.ProviderAliasAttribute";
-        private const string AliasAttibuteAliasProperty = "Alias";
 
         internal static string GetAlias(Type providerType)
         {
-            foreach (object attribute in providerType.GetTypeInfo().GetCustomAttributes(inherit: false))
+            foreach (CustomAttributeData attributeData in CustomAttributeData.GetCustomAttributes(providerType))
             {
-                if (attribute.GetType().FullName == AliasAttibuteTypeFullName)
+                if (attributeData.AttributeType.FullName == AliasAttibuteTypeFullName)
                 {
-                    PropertyInfo valueProperty = attribute
-                        .GetType()
-                        .GetProperty(AliasAttibuteAliasProperty, BindingFlags.Public | BindingFlags.Instance);
-
-                    if (valueProperty != null)
+                    foreach (CustomAttributeTypedArgument arg in attributeData.ConstructorArguments)
                     {
-                        return valueProperty.GetValue(attribute) as string;
+                        Debug.Assert(arg.ArgumentType == typeof(string));
+
+                        return arg.Value?.ToString();
                     }
                 }
             }
index 1b488e0..d0f3cc2 100644 (file)
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFrameworks>netstandard2.1;netstandard2.0;net461</TargetFrameworks>
@@ -7,6 +7,11 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\DynamicallyAccessedMembersAttribute.cs" />
+    <Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\DynamicallyAccessedMemberTypes.cs" />
+  </ItemGroup>
+
+  <ItemGroup>
     <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" />
index f1c39b3..5b8092e 100644 (file)
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using Microsoft.Extensions.DependencyInjection.Extensions;
 using Microsoft.Extensions.Hosting;
 
@@ -15,7 +16,7 @@ namespace Microsoft.Extensions.DependencyInjection
         /// <typeparam name="THostedService">An <see cref="IHostedService"/> to register.</typeparam>
         /// <param name="services">The <see cref="IServiceCollection"/> to register with.</param>
         /// <returns>The original <see cref="IServiceCollection"/>.</returns>
-        public static IServiceCollection AddHostedService<THostedService>(this IServiceCollection services)
+        public static IServiceCollection AddHostedService<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THostedService>(this IServiceCollection services)
             where THostedService : class, IHostedService
         {
             services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, THostedService>());
diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/TrimmingTests/AddHostedServiceTests.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/TrimmingTests/AddHostedServiceTests.cs
new file mode 100644 (file)
index 0000000..585eea0
--- /dev/null
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using System.Threading;
+using System.Threading.Tasks;
+
+class Program
+{
+    static int Main(string[] args)
+    {
+        IServiceCollection descriptors = new ServiceCollection();
+        descriptors.AddHostedService<MyHost>();
+
+        ServiceProvider provider = descriptors.BuildServiceProvider();
+
+        foreach (IHostedService h in provider.GetServices<IHostedService>())
+        {
+            if (!(h is MyHost))
+            {
+                return -1;
+            }
+        }
+
+        return 100;
+    }
+
+    private class MyHost : IHostedService
+    {
+        public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+        public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/TrimmingTests/Microsoft.Extensions.Hosting.TrimmingTests.proj b/src/libraries/Microsoft.Extensions.Hosting/tests/TrimmingTests/Microsoft.Extensions.Hosting.TrimmingTests.proj
new file mode 100644 (file)
index 0000000..61e8e1a
--- /dev/null
@@ -0,0 +1,13 @@
+<Project DefaultTargets="Build">
+  <Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" />
+
+  <PropertyGroup>
+    <AdditionalProjectReferences>Microsoft.Extensions.Hosting.Abstractions;Microsoft.Extensions.DependencyInjection</AdditionalProjectReferences>
+  </PropertyGroup>
+  
+  <ItemGroup>
+    <TestConsoleAppSourceFiles Include="AddHostedServiceTests.cs" />
+  </ItemGroup>
+
+  <Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets))" />
+</Project>
index 15ace04..ab21d6d 100644 (file)
@@ -13,10 +13,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Http",
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Http", "src\Microsoft.Extensions.Http.csproj", "{A50C941B-09D7-4AB4-85AF-D02E6F96885A}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Http.Tests", "tests\Microsoft.Extensions.Http.Tests.csproj", "{C9FE0B1E-35F6-4AFE-A9A6-DA6C1B88C1E7}"
-EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{92E18BB1-B630-4B03-9B05-47F31D1BF94A}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Http.Tests", "tests\Microsoft.Extensions.Http.Tests\Microsoft.Extensions.Http.Tests.csproj", "{7DADD323-891C-4EA7-8906-6BC4009FA083}"
+EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Debug|Any CPU = Debug|Any CPU
@@ -31,14 +31,14 @@ Global
                {A50C941B-09D7-4AB4-85AF-D02E6F96885A}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {A50C941B-09D7-4AB4-85AF-D02E6F96885A}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {A50C941B-09D7-4AB4-85AF-D02E6F96885A}.Release|Any CPU.Build.0 = Release|Any CPU
-               {C9FE0B1E-35F6-4AFE-A9A6-DA6C1B88C1E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-               {C9FE0B1E-35F6-4AFE-A9A6-DA6C1B88C1E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
-               {C9FE0B1E-35F6-4AFE-A9A6-DA6C1B88C1E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
-               {C9FE0B1E-35F6-4AFE-A9A6-DA6C1B88C1E7}.Release|Any CPU.Build.0 = Release|Any CPU
                {92E18BB1-B630-4B03-9B05-47F31D1BF94A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {92E18BB1-B630-4B03-9B05-47F31D1BF94A}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {92E18BB1-B630-4B03-9B05-47F31D1BF94A}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {92E18BB1-B630-4B03-9B05-47F31D1BF94A}.Release|Any CPU.Build.0 = Release|Any CPU
+               {7DADD323-891C-4EA7-8906-6BC4009FA083}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {7DADD323-891C-4EA7-8906-6BC4009FA083}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {7DADD323-891C-4EA7-8906-6BC4009FA083}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {7DADD323-891C-4EA7-8906-6BC4009FA083}.Release|Any CPU.Build.0 = Release|Any CPU
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
@@ -46,8 +46,8 @@ Global
        GlobalSection(NestedProjects) = preSolution
                {183E40F1-33A4-461D-AADF-4CF59FB93CCA} = {B51DC955-13A6-41ED-A6C7-DDAB98911513}
                {A50C941B-09D7-4AB4-85AF-D02E6F96885A} = {0517D0EB-1ED7-420A-A7D7-9A167C102ED2}
-               {C9FE0B1E-35F6-4AFE-A9A6-DA6C1B88C1E7} = {9DE482AA-076A-4EB1-821F-EE2A4A230B4A}
                {92E18BB1-B630-4B03-9B05-47F31D1BF94A} = {9DE482AA-076A-4EB1-821F-EE2A4A230B4A}
+               {7DADD323-891C-4EA7-8906-6BC4009FA083} = {9DE482AA-076A-4EB1-821F-EE2A4A230B4A}
        EndGlobalSection
        GlobalSection(ExtensibilityGlobals) = postSolution
                SolutionGuid = {F300086C-E02A-417C-8659-59AA5139E68A}
index b5d75b0..1b0f442 100644 (file)
@@ -2,13 +2,15 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using System.Net.Http;
 using System.Threading;
 using Microsoft.Extensions.DependencyInjection;
 
 namespace Microsoft.Extensions.Http
 {
-    internal class DefaultTypedHttpClientFactory<TClient> : ITypedHttpClientFactory<TClient>
+    internal class DefaultTypedHttpClientFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient> :
+        ITypedHttpClientFactory<TClient>
     {
         private readonly Cache _cache;
         private readonly IServiceProvider _services;
index f1509da..8d6ffb7 100644 (file)
@@ -4,6 +4,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Net.Http;
 using System.Threading;
@@ -316,7 +317,8 @@ namespace Microsoft.Extensions.DependencyInjection
         /// scope bound to the message handler, which is managed independently.
         /// </para>
         /// </remarks>
-        public static IHttpClientBuilder AddTypedClient<TClient>(this IHttpClientBuilder builder)
+        public static IHttpClientBuilder AddTypedClient<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient>(
+            this IHttpClientBuilder builder)
             where TClient : class
         {
             if (builder == null)
@@ -327,7 +329,8 @@ namespace Microsoft.Extensions.DependencyInjection
             return AddTypedClientCore<TClient>(builder, validateSingleType: false);
         }
 
-        internal static IHttpClientBuilder AddTypedClientCore<TClient>(this IHttpClientBuilder builder, bool validateSingleType)
+        internal static IHttpClientBuilder AddTypedClientCore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient>(
+            this IHttpClientBuilder builder, bool validateSingleType)
             where TClient : class
         {
             ReserveClient(builder, typeof(TClient), builder.Name, validateSingleType);
@@ -375,7 +378,8 @@ namespace Microsoft.Extensions.DependencyInjection
         /// scope bound to the message handler, which is managed independently.
         /// </para>
         /// </remarks>
-        public static IHttpClientBuilder AddTypedClient<TClient, TImplementation>(this IHttpClientBuilder builder)
+        public static IHttpClientBuilder AddTypedClient<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(
+            this IHttpClientBuilder builder)
             where TClient : class
             where TImplementation : class, TClient
         {
@@ -387,7 +391,8 @@ namespace Microsoft.Extensions.DependencyInjection
             return AddTypedClientCore<TClient, TImplementation>(builder, validateSingleType: false);
         }
 
-        internal static IHttpClientBuilder AddTypedClientCore<TClient, TImplementation>(this IHttpClientBuilder builder, bool validateSingleType)
+        internal static IHttpClientBuilder AddTypedClientCore<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(
+            this IHttpClientBuilder builder, bool validateSingleType)
             where TClient : class
             where TImplementation : class, TClient
         {
index d0f90cf..b0a34b7 100644 (file)
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using System.Net.Http;
 using Microsoft.Extensions.DependencyInjection.Extensions;
 using Microsoft.Extensions.Http;
@@ -198,7 +199,8 @@ namespace Microsoft.Extensions.DependencyInjection
         /// <typeparamref name="TClient"/> as the service type.
         /// </para>
         /// </remarks>
-        public static IHttpClientBuilder AddHttpClient<TClient>(this IServiceCollection services)
+        public static IHttpClientBuilder AddHttpClient<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient>(
+            this IServiceCollection services)
             where TClient : class
         {
             if (services == null)
@@ -240,7 +242,8 @@ namespace Microsoft.Extensions.DependencyInjection
         /// <typeparamref name="TClient"/> as the service type.
         /// </para>
         /// </remarks>
-        public static IHttpClientBuilder AddHttpClient<TClient, TImplementation>(this IServiceCollection services)
+        public static IHttpClientBuilder AddHttpClient<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(
+            this IServiceCollection services)
             where TClient : class
             where TImplementation : class, TClient
         {
@@ -282,7 +285,8 @@ namespace Microsoft.Extensions.DependencyInjection
         /// Use <see cref="Options.Options.DefaultName"/> as the name to configure the default client.
         /// </para>
         /// </remarks>
-        public static IHttpClientBuilder AddHttpClient<TClient>(this IServiceCollection services, string name)
+        public static IHttpClientBuilder AddHttpClient<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient>(
+            this IServiceCollection services, string name)
             where TClient : class
         {
             if (services == null)
@@ -332,7 +336,8 @@ namespace Microsoft.Extensions.DependencyInjection
         /// Use <see cref="Options.Options.DefaultName"/> as the name to configure the default client.
         /// </para>
         /// </remarks>
-        public static IHttpClientBuilder AddHttpClient<TClient, TImplementation>(this IServiceCollection services, string name)
+        public static IHttpClientBuilder AddHttpClient<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(
+            this IServiceCollection services, string name)
             where TClient : class
             where TImplementation : class, TClient
         {
@@ -376,7 +381,8 @@ namespace Microsoft.Extensions.DependencyInjection
         /// <typeparamref name="TClient"/> as the service type.
         /// </para>
         /// </remarks>
-        public static IHttpClientBuilder AddHttpClient<TClient>(this IServiceCollection services, Action<HttpClient> configureClient)
+        public static IHttpClientBuilder AddHttpClient<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient>(
+            this IServiceCollection services, Action<HttpClient> configureClient)
             where TClient : class
         {
             if (services == null)
@@ -421,7 +427,8 @@ namespace Microsoft.Extensions.DependencyInjection
         /// <typeparamref name="TClient"/> as the service type.
         /// </para>
         /// </remarks>
-        public static IHttpClientBuilder AddHttpClient<TClient>(this IServiceCollection services, Action<IServiceProvider, HttpClient> configureClient)
+        public static IHttpClientBuilder AddHttpClient<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient>(
+            this IServiceCollection services, Action<IServiceProvider, HttpClient> configureClient)
             where TClient : class
         {
             if (services == null)
@@ -470,7 +477,8 @@ namespace Microsoft.Extensions.DependencyInjection
         /// <typeparamref name="TClient"/> as the service type.
         /// </para>
         /// </remarks>
-        public static IHttpClientBuilder AddHttpClient<TClient, TImplementation>(this IServiceCollection services, Action<HttpClient> configureClient)
+        public static IHttpClientBuilder AddHttpClient<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(
+            this IServiceCollection services, Action<HttpClient> configureClient)
             where TClient : class
             where TImplementation : class, TClient
         {
@@ -520,7 +528,8 @@ namespace Microsoft.Extensions.DependencyInjection
         /// <typeparamref name="TClient"/> as the service type.
         /// </para>
         /// </remarks>
-        public static IHttpClientBuilder AddHttpClient<TClient, TImplementation>(this IServiceCollection services, Action<IServiceProvider, HttpClient> configureClient)
+        public static IHttpClientBuilder AddHttpClient<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(
+            this IServiceCollection services, Action<IServiceProvider, HttpClient> configureClient)
             where TClient : class
             where TImplementation : class, TClient
         {
@@ -569,7 +578,8 @@ namespace Microsoft.Extensions.DependencyInjection
         /// Use <see cref="Options.Options.DefaultName"/> as the name to configure the default client.
         /// </para>
         /// </remarks>
-        public static IHttpClientBuilder AddHttpClient<TClient>(this IServiceCollection services, string name, Action<HttpClient> configureClient)
+        public static IHttpClientBuilder AddHttpClient<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient>(
+            this IServiceCollection services, string name, Action<HttpClient> configureClient)
             where TClient : class
         {
             if (services == null)
@@ -621,7 +631,8 @@ namespace Microsoft.Extensions.DependencyInjection
         /// Use <see cref="Options.Options.DefaultName"/> as the name to configure the default client.
         /// </para>
         /// </remarks>
-        public static IHttpClientBuilder AddHttpClient<TClient>(this IServiceCollection services, string name, Action<IServiceProvider, HttpClient> configureClient)
+        public static IHttpClientBuilder AddHttpClient<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient>(
+            this IServiceCollection services, string name, Action<IServiceProvider, HttpClient> configureClient)
             where TClient : class
         {
             if (services == null)
@@ -677,7 +688,8 @@ namespace Microsoft.Extensions.DependencyInjection
         /// Use <see cref="Options.Options.DefaultName"/> as the name to configure the default client.
         /// </para>
         /// </remarks>
-        public static IHttpClientBuilder AddHttpClient<TClient, TImplementation>(this IServiceCollection services, string name, Action<HttpClient> configureClient)
+        public static IHttpClientBuilder AddHttpClient<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(
+            this IServiceCollection services, string name, Action<HttpClient> configureClient)
             where TClient : class
             where TImplementation : class, TClient
         {
@@ -734,7 +746,8 @@ namespace Microsoft.Extensions.DependencyInjection
         /// Use <see cref="Options.Options.DefaultName"/> as the name to configure the default client.
         /// </para>
         /// </remarks>
-        public static IHttpClientBuilder AddHttpClient<TClient, TImplementation>(this IServiceCollection services, string name, Action<IServiceProvider, HttpClient> configureClient)
+        public static IHttpClientBuilder AddHttpClient<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(
+            this IServiceCollection services, string name, Action<IServiceProvider, HttpClient> configureClient)
             where TClient : class
             where TImplementation : class, TClient
         {
index 67ac64c..979f99a 100644 (file)
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using System.Net.Http;
 using Microsoft.Extensions.DependencyInjection;
 
@@ -96,7 +97,7 @@ namespace Microsoft.Extensions.Http
     /// }
     /// </code>
     /// </example>
-    public interface ITypedHttpClientFactory<TClient>
+    public interface ITypedHttpClientFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient>
     {
         /// <summary>
         /// Creates a typed client given an associated <see cref="HttpClient"/>.
index e27e847..6de909a 100644 (file)
@@ -12,6 +12,8 @@
              Link="Common\src\Extensions\TypeNameHelper\TypeNameHelper.cs" />
     <Compile Include="$(CommonPath)Extensions\ValueStopwatch\ValueStopwatch.cs"
              Link="Common\src\Extensions\ValueStopwatch\ValueStopwatch.cs" />
+    <Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\DynamicallyAccessedMembersAttribute.cs" />
+    <Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\DynamicallyAccessedMemberTypes.cs" />
   </ItemGroup>
 
   <ItemGroup>
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFrameworks>$(NetCoreAppCurrent);net461</TargetFrameworks>
@@ -24,7 +24,7 @@
 
   <ItemGroup>
     <PackageReference Include="Moq" Version="$(MoqVersion)" />
-    <ProjectReference Include="..\src\Microsoft.Extensions.Http.csproj" SkipUseReferenceAssembly="true" />
+    <ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.Http\src\Microsoft.Extensions.Http.csproj" SkipUseReferenceAssembly="true" />
        <ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.DependencyInjection\src\Microsoft.Extensions.DependencyInjection.csproj" />
   </ItemGroup>
 
diff --git a/src/libraries/Microsoft.Extensions.Http/tests/TrimmingTests/AddTypedClientTests.cs b/src/libraries/Microsoft.Extensions.Http/tests/TrimmingTests/AddTypedClientTests.cs
new file mode 100644 (file)
index 0000000..17bcf13
--- /dev/null
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Extensions.DependencyInjection;
+using System.Net.Http;
+
+class Program
+{
+    static int Main(string[] args)
+    {
+        IServiceCollection descriptors = new ServiceCollection();
+        descriptors.AddHttpClient("test1")
+            .AddTypedClient<TypedClientA>()
+            .AddTypedClient<ITypedClientB, TypedClientB>();
+
+        descriptors.AddHttpClient<TypedClientC>("test2");
+        descriptors.AddHttpClient<ITypedClientD, TypedClientD>("test3");
+
+        ServiceProvider provider = descriptors.BuildServiceProvider();
+
+        TypedClientA clientA = provider.GetRequiredService<TypedClientA>();
+        ITypedClientB clientB = provider.GetRequiredService<ITypedClientB>();
+        TypedClientC clientC = provider.GetRequiredService<TypedClientC>();
+        ITypedClientD clientD = provider.GetRequiredService<ITypedClientD>();
+
+        if (clientA == null ||
+            !(clientB is TypedClientB) ||
+            clientC == null ||
+            !(clientD is TypedClientD))
+        {
+            return -1;
+        }
+
+        return 100;
+    }
+
+    class TypedClientA
+    {
+        public TypedClientA(HttpClient httpClient) { }
+    }
+
+    interface ITypedClientB { }
+    class TypedClientB : ITypedClientB
+    {
+        public TypedClientB(HttpClient httpClient) { }
+    }
+
+    class TypedClientC
+    {
+        public TypedClientC(HttpClient httpClient) { }
+    }
+
+    interface ITypedClientD { }
+    class TypedClientD : ITypedClientD
+    {
+        public TypedClientD(HttpClient httpClient) { }
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Http/tests/TrimmingTests/Microsoft.Extensions.Http.TrimmingTests.proj b/src/libraries/Microsoft.Extensions.Http/tests/TrimmingTests/Microsoft.Extensions.Http.TrimmingTests.proj
new file mode 100644 (file)
index 0000000..4d71019
--- /dev/null
@@ -0,0 +1,13 @@
+<Project DefaultTargets="Build">
+  <Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" />
+
+  <PropertyGroup>
+    <AdditionalProjectReferences>Microsoft.Extensions.Http</AdditionalProjectReferences>
+  </PropertyGroup>
+  
+  <ItemGroup>
+    <TestConsoleAppSourceFiles Include="AddTypedClientTests.cs" />
+  </ItemGroup>
+
+  <Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets))" />
+</Project>
index a7cab36..24a098f 100644 (file)
@@ -13,7 +13,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Loggin
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{56A299FB-3733-4AF5-AE10-66AB73D6FC13}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Logging.Console.Tests", "tests\Microsoft.Extensions.Logging.Console.Tests.csproj", "{9B344154-013F-4D6B-99A5-77DFA1CB8CC0}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Logging.Console.Tests", "tests\Microsoft.Extensions.Logging.Console.Tests\Microsoft.Extensions.Logging.Console.Tests.csproj", "{A2CFB82A-F553-44CD-8765-97409380EED9}"
 EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -29,10 +29,10 @@ Global
                {CDEDF61A-4D74-4EAA-B31E-AF5CAAA7B974}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {CDEDF61A-4D74-4EAA-B31E-AF5CAAA7B974}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {CDEDF61A-4D74-4EAA-B31E-AF5CAAA7B974}.Release|Any CPU.Build.0 = Release|Any CPU
-               {9B344154-013F-4D6B-99A5-77DFA1CB8CC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-               {9B344154-013F-4D6B-99A5-77DFA1CB8CC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
-               {9B344154-013F-4D6B-99A5-77DFA1CB8CC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
-               {9B344154-013F-4D6B-99A5-77DFA1CB8CC0}.Release|Any CPU.Build.0 = Release|Any CPU
+               {A2CFB82A-F553-44CD-8765-97409380EED9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {A2CFB82A-F553-44CD-8765-97409380EED9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {A2CFB82A-F553-44CD-8765-97409380EED9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {A2CFB82A-F553-44CD-8765-97409380EED9}.Release|Any CPU.Build.0 = Release|Any CPU
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
@@ -40,7 +40,7 @@ Global
        GlobalSection(NestedProjects) = preSolution
                {6D191D5A-572C-4BFA-B73B-C24AF80E8016} = {A12B6049-11A7-4E2B-857B-620B917B4A46}
                {CDEDF61A-4D74-4EAA-B31E-AF5CAAA7B974} = {A1F1D16A-5787-45E5-AF85-617702C216C6}
-               {9B344154-013F-4D6B-99A5-77DFA1CB8CC0} = {56A299FB-3733-4AF5-AE10-66AB73D6FC13}
+               {A2CFB82A-F553-44CD-8765-97409380EED9} = {56A299FB-3733-4AF5-AE10-66AB73D6FC13}
        EndGlobalSection
        GlobalSection(ExtensibilityGlobals) = postSolution
                SolutionGuid = {E75E4D9D-8919-409F-8FF1-67DD9C346EA7}
index 2e591ec..8cd8a59 100644 (file)
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection.Extensions;
@@ -120,7 +121,7 @@ namespace Microsoft.Extensions.Logging
         /// Adds a custom console logger formatter 'TFormatter' to be configured with options 'TOptions'.
         /// </summary>
         /// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param>
-        public static ILoggingBuilder AddConsoleFormatter<TFormatter, TOptions>(this ILoggingBuilder builder)
+        public static ILoggingBuilder AddConsoleFormatter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFormatter, TOptions>(this ILoggingBuilder builder)
             where TOptions : ConsoleFormatterOptions
             where TFormatter : ConsoleFormatter
         {
@@ -138,7 +139,7 @@ namespace Microsoft.Extensions.Logging
         /// </summary>
         /// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param>
         /// <param name="configure">A delegate to configure options 'TOptions' for custom formatter 'TFormatter'.</param>
-        public static ILoggingBuilder AddConsoleFormatter<TFormatter, TOptions>(this ILoggingBuilder builder, Action<TOptions> configure)
+        public static ILoggingBuilder AddConsoleFormatter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFormatter, TOptions>(this ILoggingBuilder builder, Action<TOptions> configure)
             where TOptions : ConsoleFormatterOptions
             where TFormatter : ConsoleFormatter
         {
index d29376c..00adb44 100644 (file)
@@ -2,11 +2,14 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using Microsoft.Extensions.Options;
 
 namespace Microsoft.Extensions.Logging.Console
 {
-    internal class FormatterOptionsMonitor<TOptions> : IOptionsMonitor<TOptions> where TOptions : ConsoleFormatterOptions
+    internal class FormatterOptionsMonitor<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TOptions> :
+        IOptionsMonitor<TOptions>
+        where TOptions : ConsoleFormatterOptions
     {
         private TOptions _options;
         public FormatterOptionsMonitor(TOptions options)
index 29bfc3f..1e8e0f4 100644 (file)
              Link="Common\Interop\Windows\Interop.GetStdHandle.cs" />
   </ItemGroup>
 
+  <ItemGroup Condition="'$(TargetFramework)' != '$(NetCoreAppCurrent)'">
+    <Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\DynamicallyAccessedMembersAttribute.cs" />
+    <Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\DynamicallyAccessedMemberTypes.cs" />
+  </ItemGroup>
+  
   <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' or
                         $(TargetFramework.StartsWith('net4'))">
     <ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Bcl.AsyncInterfaces\src\Microsoft.Bcl.AsyncInterfaces.csproj" />
diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/tests/TrimmingTests/AddConsoleFormatterTests.cs b/src/libraries/Microsoft.Extensions.Logging.Console/tests/TrimmingTests/AddConsoleFormatterTests.cs
new file mode 100644 (file)
index 0000000..e12c7d7
--- /dev/null
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using Microsoft.Extensions.Logging.Console;
+using System.IO;
+
+class Program
+{
+    private static int ConstructorCallCount = 0;
+    private static int WriteCallCount = 0;
+
+    static int Main(string[] args)
+    {
+        IServiceCollection descriptors = new ServiceCollection();
+        descriptors.AddLogging(builder =>
+        {
+            builder.AddConsoleFormatter<CustomFormatter, CustomOptions>();
+            builder.AddConsole(o => { o.FormatterName = "custom"; });
+        });
+
+        ServiceProvider provider = descriptors.BuildServiceProvider();
+
+        ILoggerProvider logger = provider.GetRequiredService<ILoggerProvider>();
+        logger.CreateLogger("log").LogError("Hello");
+
+        if (ConstructorCallCount != 1 ||
+            WriteCallCount != 1)
+        {
+            return -1;
+        }
+
+        return 100;
+    }
+
+    private class CustomFormatter : ConsoleFormatter
+    {
+        public CustomFormatter() : base("Custom")
+        {
+            ConstructorCallCount++;
+        }
+        public override void Write<TState>(in LogEntry<TState> logEntry, IExternalScopeProvider scopeProvider, TextWriter textWriter)
+        {
+            WriteCallCount++;
+        }
+    }
+
+    private class CustomOptions : ConsoleFormatterOptions { }
+}
diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/tests/TrimmingTests/Microsoft.Extensions.Logging.Console.TrimmingTests.proj b/src/libraries/Microsoft.Extensions.Logging.Console/tests/TrimmingTests/Microsoft.Extensions.Logging.Console.TrimmingTests.proj
new file mode 100644 (file)
index 0000000..353b9d1
--- /dev/null
@@ -0,0 +1,13 @@
+<Project DefaultTargets="Build">
+  <Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" />
+
+  <PropertyGroup>
+    <AdditionalProjectReferences>Microsoft.Extensions.Logging.Console</AdditionalProjectReferences>
+  </PropertyGroup>
+  
+  <ItemGroup>
+    <TestConsoleAppSourceFiles Include="AddConsoleFormatterTests.cs" />
+  </ItemGroup>
+
+  <Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets))" />
+</Project>
index 75ce085..9ba9856 100644 (file)
@@ -11,6 +11,7 @@
     <TestAssemblies Condition="'$(TestAssemblies)' == ''">true</TestAssemblies>
     <TestPackages Condition="'$(TestPackages)' == ''">false</TestPackages>
     <TestTrimming Condition="'$(TestTrimming)' == ''">false</TestTrimming>
+    <ContinueOnError Condition="'$(TestTrimming)' == 'true'">true</ContinueOnError>
   </PropertyGroup>
   
   <!-- Projects that don't support code coverage measurement. -->
@@ -38,6 +39,7 @@
                       Condition="'$(TestPackages)' == 'true'" />
     <ProjectReference Include="$(MSBuildThisFileDirectory)*\tests\**\*.TrimmingTests.proj"
                       Exclude="@(ProjectExclusions)"
+                      BuildInParallel="false"
                       Condition="'$(TestTrimming)' == 'true'" />
   </ItemGroup>