Skip to content

Commit

Permalink
[Extensions] Add AOT compatibility attributes
Browse files Browse the repository at this point in the history
Annotate the Microsoft.Extensions.Azure library for trimming/AOT compatibility.

Also add a few suppressions to Azure.Identity's EventSource that showed up when I was using Microsoft.Extensions.Azure in a real-world app.
  • Loading branch information
eerhardt committed Feb 13, 2024
1 parent 88b55fd commit 1436ca5
Show file tree
Hide file tree
Showing 15 changed files with 120 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Diagnostics.CodeAnalysis;

namespace Azure.Core.Extensions
{
Expand All @@ -17,6 +18,6 @@ public interface IAzureClientFactoryBuilder
/// <typeparam name="TOptions">The client options type used the client.</typeparam>
/// <param name="clientFactory">The factory, that given the instance of options, returns a client instance.</param>
/// <returns><see cref="IAzureClientBuilder{TClient,TOptions}"/> that allows customizing the client registration.</returns>
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, TOptions>(Func<TOptions, TClient> clientFactory) where TOptions : class;
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(Func<TOptions, TClient> clientFactory) where TOptions : class;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Diagnostics.CodeAnalysis;

namespace Azure.Core.Extensions
{
/// <summary>
Expand All @@ -15,6 +17,8 @@ public interface IAzureClientFactoryBuilderWithConfiguration<in TConfiguration>
/// <typeparam name="TOptions">The client options type used the client.</typeparam>
/// <param name="configuration">Instance of <typeparamref name="TConfiguration"/> to use.</param>
/// <returns><see cref="IAzureClientBuilder{TClient,TOptions}"/> that allows customizing the client registration.</returns>
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, TOptions>(TConfiguration configuration) where TOptions : class;
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
[RequiresDynamicCode("Binding strongly typed objects to configuration values requires generating dynamic code at runtime, for example instantiating generic types. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(TConfiguration configuration) where TOptions : class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Diagnostics.CodeAnalysis;

namespace Azure.Core.Extensions
{
Expand All @@ -18,6 +19,6 @@ public interface IAzureClientFactoryBuilderWithCredential
/// <param name="clientFactory">The factory, that given the instance of options and credential, returns a client instance.</param>
/// <param name="requiresCredential">Specifies whether the credential is optional (client supports anonymous authentication).</param>
/// <returns><see cref="IAzureClientBuilder{TClient,TOptions}"/> that allows customizing the client registration.</returns>
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, TOptions>(Func<TOptions, TokenCredential, TClient> clientFactory, bool requiresCredential = true) where TOptions : class;
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(Func<TOptions, TokenCredential, TClient> clientFactory, bool requiresCredential = true) where TOptions : class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Diagnostics.CodeAnalysis;

namespace Microsoft.Extensions.Azure
{
Expand Down Expand Up @@ -78,6 +79,8 @@ public static IAzureClientBuilder<TClient, TOptions> ConfigureOptions<TClient, T
/// <param name="builder">The client builder instance.</param>
/// <param name="configuration">The configuration instance to use.</param>
/// <returns>The client builder instance.</returns>
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
[RequiresDynamicCode("Binding strongly typed objects to configuration values requires generating dynamic code at runtime, for example instantiating generic types. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
public static IAzureClientBuilder<TClient, TOptions> ConfigureOptions<TClient, TOptions>(this IAzureClientBuilder<TClient, TOptions> builder, IConfiguration configuration) where TOptions : class
{
return builder.ConfigureOptions(options => configuration.Bind(options));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Diagnostics.CodeAnalysis;
using Azure.Core;
using Azure.Core.Extensions;
using Microsoft.Extensions.Configuration;
Expand All @@ -25,12 +26,16 @@ internal AzureClientFactoryBuilder(IServiceCollection serviceCollection)
_serviceCollection = serviceCollection;
}

IAzureClientBuilder<TClient, TOptions> IAzureClientFactoryBuilder.RegisterClientFactory<TClient, TOptions>(Func<TOptions, TClient> clientFactory)
IAzureClientBuilder<TClient, TOptions> IAzureClientFactoryBuilder.RegisterClientFactory<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
Func<TOptions, TClient> clientFactory)
{
return ((IAzureClientFactoryBuilderWithCredential)this).RegisterClientFactory<TClient, TOptions>((options, _) => clientFactory(options));
}

IAzureClientBuilder<TClient, TOptions> IAzureClientFactoryBuilderWithConfiguration<IConfiguration>.RegisterClientFactory<TClient, TOptions>(IConfiguration configuration)
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
[RequiresDynamicCode("Binding strongly typed objects to configuration values requires generating dynamic code at runtime, for example instantiating generic types. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
IAzureClientBuilder<TClient, TOptions> IAzureClientFactoryBuilderWithConfiguration<IConfiguration>.RegisterClientFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
IConfiguration configuration)
{
var credentialsFromConfig = ClientFactory.CreateCredential(configuration);
var clientBuilder =((IAzureClientFactoryBuilderWithCredential)this).RegisterClientFactory<TClient, TOptions>(
Expand Down Expand Up @@ -73,6 +78,8 @@ public AzureClientFactoryBuilder ConfigureDefaults(Action<ClientOptions, IServic
/// </summary>
/// <param name="configuration">The configuration instance.</param>
/// <returns>This instance.</returns>
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
[RequiresDynamicCode("Binding strongly typed objects to configuration values requires generating dynamic code at runtime, for example instantiating generic types. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
public AzureClientFactoryBuilder ConfigureDefaults(IConfiguration configuration)
{
ConfigureDefaults(configuration.Bind);
Expand All @@ -87,7 +94,8 @@ public AzureClientFactoryBuilder ConfigureDefaults(IConfiguration configuration)
return this;
}

IAzureClientBuilder<TClient, TOptions> IAzureClientFactoryBuilderWithCredential.RegisterClientFactory<TClient, TOptions>(Func<TOptions, TokenCredential, TClient> clientFactory, bool requiresCredential)
IAzureClientBuilder<TClient, TOptions> IAzureClientFactoryBuilderWithCredential.RegisterClientFactory<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
Func<TOptions, TokenCredential, TClient> clientFactory, bool requiresCredential)
{
return RegisterClientFactory<TClient, TOptions>((options, credential, _) => clientFactory(options, credential), requiresCredential);
}
Expand Down Expand Up @@ -119,7 +127,9 @@ public AzureClientFactoryBuilder UseCredential(Func<IServiceProvider, TokenCrede
/// <typeparam name="TClient">The type of the client.</typeparam>
/// <typeparam name="TOptions">The type of the client options.</typeparam>
/// <returns>The <see cref="IAzureClientBuilder{TClient, TOptions}"/> to allow client configuration.</returns>
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, TOptions>(Func<TOptions, TClient> factory) where TOptions : class
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
Func<TOptions, TClient> factory)
where TOptions : class
{
return RegisterClientFactory<TClient, TOptions>((options, _, _) => factory(options), false);
}
Expand All @@ -130,7 +140,9 @@ public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, TOptions>(Func<
/// <typeparam name="TClient">The type of the client.</typeparam>
/// <typeparam name="TOptions">The type of the client options.</typeparam>
/// <returns>The <see cref="IAzureClientBuilder{TClient, TOptions}"/> to allow client configuration.</returns>
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, TOptions>(Func<TOptions, TokenCredential, TClient> factory) where TOptions : class
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
Func<TOptions, TokenCredential, TClient> factory)
where TOptions : class
{
return RegisterClientFactory<TClient, TOptions>((options, credential, _) => factory(options, credential), true);
}
Expand All @@ -142,7 +154,9 @@ public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, TOptions>(Func<
/// <typeparam name="TClient">The type of the client.</typeparam>
/// <typeparam name="TOptions">The type of the client options.</typeparam>
/// <returns>The <see cref="IAzureClientBuilder{TClient, TOptions}"/> to allow client configuration.</returns>
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, TOptions>(Func<TOptions, IServiceProvider, TClient> factory) where TOptions : class
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
Func<TOptions, IServiceProvider, TClient> factory)
where TOptions : class
{
return RegisterClientFactory<TClient, TOptions>((options, _, provider) => factory(options, provider), true);
}
Expand All @@ -154,12 +168,17 @@ public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, TOptions>(Func<
/// <typeparam name="TClient">The type of the client.</typeparam>
/// <typeparam name="TOptions">The type of the client options.</typeparam>
/// <returns>The <see cref="IAzureClientBuilder{TClient, TOptions}"/> to allow client configuration.</returns>
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, TOptions>(Func<TOptions, TokenCredential, IServiceProvider, TClient> factory) where TOptions : class
public IAzureClientBuilder<TClient, TOptions> AddClient<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
Func<TOptions, TokenCredential, IServiceProvider, TClient> factory)
where TOptions : class
{
return RegisterClientFactory<TClient, TOptions>((options, credential, provider) => factory(options, credential, provider), true);
}

private IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, TOptions>(Func<TOptions, TokenCredential, IServiceProvider, TClient> clientFactory, bool requiresCredential) where TOptions : class
private IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>(
Func<TOptions, TokenCredential, IServiceProvider, TClient> clientFactory,
bool requiresCredential)
where TOptions : class
{
var clientRegistration = new ClientRegistration<TClient>(DefaultClientName, requiresCredential, (provider, options, credential) => clientFactory((TOptions) options, credential, provider));
_serviceCollection.AddSingleton(clientRegistration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Diagnostics.CodeAnalysis;
using Azure.Core;
using Microsoft.Extensions.Configuration;

Expand All @@ -24,7 +25,12 @@ public abstract class AzureComponentFactory
/// <param name="serviceVersion">The value of ServiceVersion enum to use, null to use the default.</param>
/// <param name="configuration">The <see cref="IConfiguration"/> instance to apply to options.</param>
/// <returns>A new instance of <paramref name="optionsType"/>.</returns>
public abstract object CreateClientOptions(Type optionsType, object serviceVersion, IConfiguration configuration);
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
[RequiresDynamicCode("Binding strongly typed objects to configuration values requires generating dynamic code at runtime, for example instantiating generic types. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
public abstract object CreateClientOptions(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type optionsType,
object serviceVersion,
IConfiguration configuration);

/// <summary>
/// Creates a new client instance using the provided configuration to map constructor parameters from.
Expand All @@ -35,6 +41,11 @@ public abstract class AzureComponentFactory
/// <param name="credential">The <see cref="TokenCredential"/> object to use if required by constructor, if null no .</param>
/// <param name="clientOptions">The client </param>
/// <returns></returns>
public abstract object CreateClient(Type clientType, IConfiguration configuration, TokenCredential credential, object clientOptions);
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
public abstract object CreateClient(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type clientType,
IConfiguration configuration,
TokenCredential credential,
object clientOptions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;

namespace Microsoft.Extensions.Azure
{
internal class AzureClientFactory<TClient, TOptions>: IAzureClientFactory<TClient>, IDisposable, IAsyncDisposable
internal class AzureClientFactory<TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TOptions>
: IAzureClientFactory<TClient>, IDisposable, IAsyncDisposable
{
private readonly Dictionary<string, ClientRegistration<TClient>> _clientRegistrations;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Diagnostics.CodeAnalysis;
using Azure.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
Expand All @@ -25,7 +26,12 @@ public override TokenCredential CreateTokenCredential(IConfiguration configurati
return ClientFactory.CreateCredential(configuration) ?? _globalOptions.CurrentValue.CredentialFactory(_serviceProvider);
}

public override object CreateClientOptions(Type optionsType, object serviceVersion, IConfiguration configuration)
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
[RequiresDynamicCode("Binding strongly typed objects to configuration values requires generating dynamic code at runtime, for example instantiating generic types. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
public override object CreateClientOptions(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type optionsType,
object serviceVersion,
IConfiguration configuration)
{
if (optionsType == null) throw new ArgumentNullException(nameof(optionsType));
var options = ClientFactory.CreateClientOptions(serviceVersion, optionsType);
Expand All @@ -41,7 +47,12 @@ public override object CreateClientOptions(Type optionsType, object serviceVersi
return options;
}

public override object CreateClient(Type clientType, IConfiguration configuration, TokenCredential credential, object clientOptions)
[RequiresUnreferencedCode("Binding strongly typed objects to configuration values is not supported with trimming. Use the Configuration Binder Source Generator (EnableConfigurationBindingGenerator=true) instead.")]
public override object CreateClient(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type clientType,
IConfiguration configuration,
TokenCredential credential,
object clientOptions)
{
if (clientType == null) throw new ArgumentNullException(nameof(clientType));
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
Expand Down
Loading

0 comments on commit 1436ca5

Please sign in to comment.