Skip to content

[BUG] AzureClientServiceCollectionExtensions.AddAzureClients attempts to read from config values before validation has occurred #48584

@halestock

Description

@halestock

Library name and version

Microsoft.Extensions.Azure 1.10.0

Describe the bug

In our application we use the Options pattern for strongly-typed config, with validation:

var options = builder.Services
  .AddOptionsWithValidateOnStart<ApplicationConfig>()
  .ValidateDataAnnotations()
  .Bind(builder.Configuration);

We then reference it during application startup using the following code:

var config = builder.Configuration.Get<ApplicationConfig>();

We then use the AddAzureClients method to register azure clients using the config object:

services.AddAzureClients(x =>
{
    x.UseCredential(new DefaultAzureCredential());
    x.AddBlobServiceClient(new Uri(config.BlobStorageUrl));
});

However, the lambda that is passed as the Action<AzureClientFactoryBuilder> configureClients parameter is immediately invoked, and so if e.g. config.BlobStorageUrl is null, a ArgumentNullException will be thrown. I would normally expect this to occur after validation has happened, so that we would instead get a validation failure that the required config key is missing or invalid.

Expected behavior

Normally, when injecting clients, the configure action is not invoked until after the StartupValidator has run and successfully validated the config options. This is the case when normally injecting services, e.g.

services.AddDbContextFactory<ApplicationDbContext>(dbOptions =>
{
    dbOptions.UseSqlServer(config.SqlDbConnectionString);
}

In that case, if a required config value were missing, we would see e.g.

Microsoft.Extensions.Options.OptionsValidationException: DataAnnotation validation failed for 'ApplicationConfig' members: 'BlobStorageUrl' with the error: 'The BlobStorageUrl field is required.'

Actual behavior

On startup, an ArgumentNullException is thrown without reference to the specific config value that is missing:

System.ArgumentNullException: Value cannot be null. (Parameter 'uriString')

Reproduction Steps

Abbreviated Program.cs:

var builder = WebApplication.CreateBuilder(args);
var options = builder.Services
  .AddOptionsWithValidateOnStart<ApplicationConfig>()
  .ValidateDataAnnotations()
  .Bind(builder.Configuration);

var config = builder.Configuration.Get<ApplicationConfig>();

builder.Services.AddAzureClients(x =>
{
  x.AddBlobServiceClient(new Uri(config.BlobStorageUrl));
});

var app = builder.Build();

// other startup code

app.Run();

public class ApplicationConfig
{
  [Required]
  [ConfigurationKeyName("BlobStorageUrl")]
  public required string BlobStorageUrl { get; init; }
}

appsettings.json:

{
  "BlobStorageUrl": null
}

Environment

.NET SDK:
 Version:           9.0.100
 Commit:            59db016f11
 Workload version:  9.0.100-manifests.3068a692
 MSBuild version:   17.12.7+5b8665660

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.26100
 OS Platform: Windows
 RID:         win-x64

Microsoft Visual Studio Enterprise 2022 (64-bit) - Current
Version 17.11.6

Metadata

Metadata

Assignees

No one assigned

    Labels

    ClientThis issue is related to a non-management packageExtensionsASP.NET Core extensionscustomer-reportedIssues that are reported by GitHub users external to the Azure organization.feature-requestThis issue requires a new behavior in the product in order be resolved.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions