* Make Extensions linker friendly.
Annotating the rest of the Microsoft.Extensions libraries.
Fix #40396
* Disable parallelism in trimming tests to fix race condition.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Diagnostics;
using System.Reflection;
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();
}
}
}
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.1;netstandard2.0;net461</TargetFrameworks>
</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" />
// 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;
/// <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>());
--- /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 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;
+ }
+}
--- /dev/null
+<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>
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
{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
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}
// 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;
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net.Http;
using System.Threading;
/// 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)
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);
/// 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
{
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
{
// 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;
/// <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)
/// <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
{
/// 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)
/// 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
{
/// <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)
/// <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)
/// <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
{
/// <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
{
/// 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)
/// 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)
/// 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
{
/// 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
{
// 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;
/// }
/// </code>
/// </example>
- public interface ITypedHttpClientFactory<TClient>
+ public interface ITypedHttpClientFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient>
{
/// <summary>
/// Creates a typed client given an associated <see cref="HttpClient"/>.
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>
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);net461</TargetFrameworks>
<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>
--- /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 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) { }
+ }
+}
--- /dev/null
+<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>
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
{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
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}
// 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;
/// 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
{
/// </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
{
// 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)
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" />
</PropertyGroup>
<ItemGroup>
- <ProjectReference Include="..\src\Microsoft.Extensions.Logging.Console.csproj" SkipUseReferenceAssembly="true" />
+ <ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.Logging.Console\src\Microsoft.Extensions.Logging.Console.csproj" SkipUseReferenceAssembly="true" />
</ItemGroup>
</Project>
\ No newline at end of file
--- /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 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 { }
+}
--- /dev/null
+<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>
<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. -->
Condition="'$(TestPackages)' == 'true'" />
<ProjectReference Include="$(MSBuildThisFileDirectory)*\tests\**\*.TrimmingTests.proj"
Exclude="@(ProjectExclusions)"
+ BuildInParallel="false"
Condition="'$(TestTrimming)' == 'true'" />
</ItemGroup>