Skip to content

Commit a4f8c03

Browse files
committed
Clean up
1 parent e7b4e7e commit a4f8c03

File tree

3 files changed

+115
-80
lines changed

3 files changed

+115
-80
lines changed

src/Aspire.Hosting.Azure.Storage/AzureStorageExtensions.cs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -132,25 +132,24 @@ public static IResourceBuilder<AzureStorageResource> RunAsEmulator(this IResourc
132132
});
133133

134134
BlobServiceClient? blobServiceClient = null;
135+
135136
builder.ApplicationBuilder.Eventing.Subscribe<BeforeResourceStartedEvent>(builder.Resource, async (@event, ct) =>
136137
{
137138
// The BlobServiceClient is created before the health check is run.
138139
// We can't use ConnectionStringAvailableEvent here because the resource doesn't have a connection string, so
139140
// we use BeforeResourceStartedEvent
140141

141-
var connectionString = await builder.Resource.GetBlobConnectionString().GetValueAsync(ct).ConfigureAwait(false) ?? throw new DistributedApplicationException($"{nameof(ConnectionStringAvailableEvent)} was published for the '{builder.Resource.Name}' resource but the connection string was null.");
142+
var connectionString = await builder.Resource.GetBlobConnectionString().GetValueAsync(ct).ConfigureAwait(false)
143+
?? throw new DistributedApplicationException($"{nameof(ConnectionStringAvailableEvent)} was published for the '{builder.Resource.Name}' resource but the connection string was null.");
142144
blobServiceClient = CreateBlobServiceClient(connectionString);
143145
});
144146

145147
builder.ApplicationBuilder.Eventing.Subscribe<ResourceReadyEvent>(builder.Resource, async (@event, ct) =>
146148
{
147-
// The ResourceReadyEvent of a resource is triggered after its health check is healthy.
149+
// The ResourceReadyEvent of a resource is triggered after its health check (AddAzureBlobStorage) is healthy.
148150
// This means we can safely use this event to create the blob containers.
149151

150-
if (blobServiceClient is null)
151-
{
152-
throw new InvalidOperationException("BlobServiceClient is not initialized.");
153-
}
152+
_ = blobServiceClient ?? throw new InvalidOperationException($"{nameof(BlobServiceClient)} is not initialized.");
154153

155154
foreach (var container in builder.Resource.BlobContainers)
156155
{
@@ -159,6 +158,7 @@ public static IResourceBuilder<AzureStorageResource> RunAsEmulator(this IResourc
159158
}
160159
});
161160

161+
// Add the "Storage" resource health check. There will be separate health checks for the nested child resources.
162162
var healthCheckKey = $"{builder.Resource.Name}_check";
163163

164164
builder.ApplicationBuilder.Services.AddHealthChecks().AddAzureBlobStorage(sp =>
@@ -284,7 +284,7 @@ public static IResourceBuilder<AzureStorageEmulatorResource> WithApiVersionCheck
284284
/// <summary>
285285
/// Creates a builder for the <see cref="AzureBlobStorageResource"/> which can be referenced to get the Azure Storage blob endpoint for the storage account.
286286
/// </summary>
287-
/// <param name="builder">The <see cref="IResourceBuilder{T}"/> for <see cref="AzureStorageResource"/>/</param>
287+
/// <param name="builder">The <see cref="IResourceBuilder{T}"/> for <see cref="AzureStorageResource"/>.</param>
288288
/// <param name="name">The name of the resource.</param>
289289
/// <returns>An <see cref="IResourceBuilder{T}"/> for the <see cref="AzureBlobStorageResource"/>.</returns>
290290
public static IResourceBuilder<AzureBlobStorageResource> AddBlobs(this IResourceBuilder<AzureStorageResource> builder, [ResourceName] string name)
@@ -300,6 +300,8 @@ public static IResourceBuilder<AzureBlobStorageResource> AddBlobs(this IResource
300300
connectionString = await resource.ConnectionStringExpression.GetValueAsync(ct).ConfigureAwait(false);
301301
});
302302

303+
// Add the "Blobs" resource health check. This is a separate health check from the "Storage" resource health check.
304+
// Doing it on the storage is not sufficient as the WaitForHealthyAsync doesn't bubble up to the parent resources.
303305
var healthCheckKey = $"{resource.Name}_check";
304306

305307
BlobServiceClient? blobServiceClient = null;
@@ -308,13 +310,15 @@ public static IResourceBuilder<AzureBlobStorageResource> AddBlobs(this IResource
308310
return blobServiceClient ??= CreateBlobServiceClient(connectionString ?? throw new InvalidOperationException("Connection string is not initialized."));
309311
}, name: healthCheckKey);
310312

311-
return builder.ApplicationBuilder.AddResource(resource).WithHealthCheck(healthCheckKey);
313+
return builder.ApplicationBuilder
314+
.AddResource(resource)
315+
.WithHealthCheck(healthCheckKey);
312316
}
313317

314318
/// <summary>
315319
/// Creates a builder for the <see cref="AzureBlobStorageContainerResource"/> which can be referenced to get the Azure Storage blob container endpoint for the storage account.
316320
/// </summary>
317-
/// <param name="builder">The <see cref="IResourceBuilder{T}"/> for <see cref="AzureBlobStorageResource"/>/</param>
321+
/// <param name="builder">The <see cref="IResourceBuilder{T}"/> for <see cref="AzureBlobStorageResource"/>.</param>
318322
/// <param name="name">The name of the resource.</param>
319323
/// <param name="blobContainerName">The name of the blob container.</param>
320324
/// <returns>An <see cref="IResourceBuilder{T}"/> for the <see cref="AzureBlobStorageContainerResource"/>.</returns>
@@ -343,13 +347,14 @@ public static IResourceBuilder<AzureBlobStorageContainerResource> AddBlobContain
343347
name: healthCheckKey);
344348

345349
return builder.ApplicationBuilder
346-
.AddResource(resource).WithHealthCheck(healthCheckKey);
350+
.AddResource(resource)
351+
.WithHealthCheck(healthCheckKey);
347352
}
348353

349354
/// <summary>
350355
/// Creates a builder for the <see cref="AzureTableStorageResource"/> which can be referenced to get the Azure Storage tables endpoint for the storage account.
351356
/// </summary>
352-
/// <param name="builder">The <see cref="IResourceBuilder{T}"/> for <see cref="AzureStorageResource"/>/</param>
357+
/// <param name="builder">The <see cref="IResourceBuilder{T}"/> for <see cref="AzureStorageResource"/>.</param>
353358
/// <param name="name">The name of the resource.</param>
354359
/// <returns>An <see cref="IResourceBuilder{T}"/> for the <see cref="AzureTableStorageResource"/>.</returns>
355360
public static IResourceBuilder<AzureTableStorageResource> AddTables(this IResourceBuilder<AzureStorageResource> builder, [ResourceName] string name)
@@ -364,7 +369,7 @@ public static IResourceBuilder<AzureTableStorageResource> AddTables(this IResour
364369
/// <summary>
365370
/// Creates a builder for the <see cref="AzureQueueStorageResource"/> which can be referenced to get the Azure Storage queues endpoint for the storage account.
366371
/// </summary>
367-
/// <param name="builder">The <see cref="IResourceBuilder{T}"/> for <see cref="AzureStorageResource"/>/</param>
372+
/// <param name="builder">The <see cref="IResourceBuilder{T}"/> for <see cref="AzureStorageResource"/>.</param>
368373
/// <param name="name">The name of the resource.</param>
369374
/// <returns>An <see cref="IResourceBuilder{T}"/> for the <see cref="AzureQueueStorageResource"/>.</returns>
370375
public static IResourceBuilder<AzureQueueStorageResource> AddQueues(this IResourceBuilder<AzureStorageResource> builder, [ResourceName] string name)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Aspire.Azure.Common;
5+
using Aspire.Azure.Storage.Queues;
6+
using Azure.Core;
7+
using Azure.Core.Extensions;
8+
using Azure.Storage.Queues;
9+
using HealthChecks.Azure.Storage.Queues;
10+
using Microsoft.Extensions.Azure;
11+
using Microsoft.Extensions.Configuration;
12+
using Microsoft.Extensions.DependencyInjection;
13+
using Microsoft.Extensions.Diagnostics.HealthChecks;
14+
15+
namespace Microsoft.Extensions.Hosting;
16+
17+
public static partial class AspireQueueStorageExtensions
18+
{
19+
private sealed class StorageQueueComponent : AzureComponent<AzureStorageQueuesSettings, QueueServiceClient, QueueClientOptions>
20+
{
21+
protected override IAzureClientBuilder<QueueServiceClient, QueueClientOptions> AddClient(
22+
AzureClientFactoryBuilder azureFactoryBuilder, AzureStorageQueuesSettings settings, string connectionName,
23+
string configurationSectionName)
24+
{
25+
return ((IAzureClientFactoryBuilderWithCredential)azureFactoryBuilder).RegisterClientFactory<QueueServiceClient, QueueClientOptions>((options, cred) =>
26+
{
27+
var connectionString = settings.ConnectionString;
28+
if (string.IsNullOrEmpty(connectionString) && settings.ServiceUri is null)
29+
{
30+
throw new InvalidOperationException($"A QueueServiceClient could not be configured. Ensure valid connection information was provided in 'ConnectionStrings:{connectionName}' or specify a 'ConnectionString' or 'ServiceUri' in the '{configurationSectionName}' configuration section.");
31+
}
32+
33+
return !string.IsNullOrEmpty(connectionString)
34+
? new QueueServiceClient(connectionString, options)
35+
: cred is not null
36+
? new QueueServiceClient(settings.ServiceUri, cred, options)
37+
: new QueueServiceClient(settings.ServiceUri, options);
38+
}, requiresCredential: false);
39+
}
40+
41+
protected override void BindClientOptionsToConfiguration(IAzureClientBuilder<QueueServiceClient, QueueClientOptions> clientBuilder, IConfiguration configuration)
42+
{
43+
#pragma warning disable IDE0200 // Remove unnecessary lambda expression - needed so the ConfigBinder Source Generator works
44+
clientBuilder.ConfigureOptions(options => configuration.Bind(options));
45+
#pragma warning restore IDE0200
46+
}
47+
48+
protected override void BindSettingsToConfiguration(AzureStorageQueuesSettings settings, IConfiguration configuration)
49+
{
50+
configuration.Bind(settings);
51+
}
52+
53+
protected override IHealthCheck CreateHealthCheck(QueueServiceClient client, AzureStorageQueuesSettings settings)
54+
=> new AzureQueueStorageHealthCheck(client, new AzureQueueStorageHealthCheckOptions());
55+
56+
protected override bool GetHealthCheckEnabled(AzureStorageQueuesSettings settings)
57+
=> !settings.DisableHealthChecks;
58+
59+
protected override TokenCredential? GetTokenCredential(AzureStorageQueuesSettings settings)
60+
=> settings.Credential;
61+
62+
protected override bool GetMetricsEnabled(AzureStorageQueuesSettings settings)
63+
=> false;
64+
65+
protected override bool GetTracingEnabled(AzureStorageQueuesSettings settings)
66+
=> !settings.DisableTracing;
67+
}
68+
}
Lines changed: 30 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,38 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using Aspire.Azure.Common;
54
using Aspire.Azure.Storage.Queues;
6-
using Azure.Core;
75
using Azure.Core.Extensions;
86
using Azure.Storage.Queues;
9-
using HealthChecks.Azure.Storage.Queues;
10-
using Microsoft.Extensions.Azure;
11-
using Microsoft.Extensions.Configuration;
127
using Microsoft.Extensions.DependencyInjection;
13-
using Microsoft.Extensions.Diagnostics.HealthChecks;
148

159
namespace Microsoft.Extensions.Hosting;
1610

1711
/// <summary>
1812
/// Provides extension methods for registering <see cref="QueueServiceClient"/> as a singleton in the services provided by the <see cref="IHostApplicationBuilder"/>.
1913
/// Enables retries, corresponding health check, logging and telemetry.
2014
/// </summary>
21-
public static class AspireQueueStorageExtensions
15+
public static partial class AspireQueueStorageExtensions
2216
{
2317
private const string DefaultConfigSectionName = "Aspire:Azure:Storage:Queues";
2418

2519
/// <summary>
26-
/// Registers <see cref="QueueServiceClient"/> as a singleton in the services provided by the <paramref name="builder"/>.
27-
/// Enables retries, corresponding health check, logging and telemetry.
20+
/// Registers <see cref="QueueServiceClient"/> as a singleton in the services provided by the <paramref name="builder"/>.
21+
/// Enables retries, corresponding health check, logging and telemetry.
2822
/// </summary>
2923
/// <param name="builder">The <see cref="IHostApplicationBuilder" /> to read config from and add services to.</param>
3024
/// <param name="connectionName">A name used to retrieve the connection string from the ConnectionStrings configuration section.</param>
31-
/// <param name="configureSettings">An optional method that can be used for customizing the <see cref="AzureStorageQueuesSettings"/>. It's invoked after the settings are read from the configuration.</param>
32-
/// <param name="configureClientBuilder">An optional method that can be used for customizing the <see cref="IAzureClientBuilder{TClient, TOptions}"/>.</param>
25+
/// <param name="configureSettings">
26+
/// An optional method that can be used for customizing the <see cref="AzureStorageQueuesSettings"/>. It's invoked after
27+
/// the settings are read from the configuration.
28+
/// </param>
29+
/// <param name="configureClientBuilder">
30+
/// An optional method that can be used for customizing the <see cref="IAzureClientBuilder{TClient, TOptions}"/>.
31+
/// </param>
3332
/// <remarks>Reads the configuration from "Aspire:Azure:Storage:Queues" section.</remarks>
34-
/// <exception cref="InvalidOperationException">Thrown when neither <see cref="AzureStorageQueuesSettings.ConnectionString"/> nor <see cref="AzureStorageQueuesSettings.ServiceUri"/> is provided.</exception>
33+
/// <exception cref="InvalidOperationException">
34+
/// Neither <see cref="AzureStorageQueuesSettings.ConnectionString"/> nor <see cref="AzureStorageQueuesSettings.ServiceUri"/> is provided.
35+
/// </exception>
3536
public static void AddAzureQueueClient(
3637
this IHostApplicationBuilder builder,
3738
string connectionName,
@@ -45,15 +46,26 @@ public static void AddAzureQueueClient(
4546
}
4647

4748
/// <summary>
48-
/// Registers <see cref="QueueServiceClient"/> as a singleton for given <paramref name="name"/> in the services provided by the <paramref name="builder"/>.
49-
/// Enables retries, corresponding health check, logging and telemetry.
49+
/// Registers <see cref="QueueServiceClient"/> as a singleton for given <paramref name="name"/> in the services provided
50+
/// by the <paramref name="builder"/>.
51+
/// Enables retries, corresponding health check, logging and telemetry.
5052
/// </summary>
5153
/// <param name="builder">The <see cref="IHostApplicationBuilder" /> to read config from and add services to.</param>
52-
/// <param name="name">The name of the component, which is used as the <see cref="ServiceDescriptor.ServiceKey"/> of the service and also to retrieve the connection string from the ConnectionStrings configuration section.</param>
53-
/// <param name="configureSettings">An optional method that can be used for customizing the <see cref="AzureStorageQueuesSettings"/>. It's invoked after the settings are read from the configuration.</param>
54-
/// <param name="configureClientBuilder">An optional method that can be used for customizing the <see cref="IAzureClientBuilder{TClient, TOptions}"/>.</param>
54+
/// <param name="name">
55+
/// The name of the component, which is used as the <see cref="ServiceDescriptor.ServiceKey"/> of the service
56+
/// and also to retrieve the connection string from the ConnectionStrings configuration section.
57+
/// </param>
58+
/// <param name="configureSettings">
59+
/// An optional method that can be used for customizing the <see cref="AzureStorageQueuesSettings"/>.
60+
/// It's invoked after the settings are read from the configuration.
61+
/// </param>
62+
/// <param name="configureClientBuilder">
63+
/// An optional method that can be used for customizing the <see cref="IAzureClientBuilder{TClient, TOptions}"/>.
64+
/// </param>
5565
/// <remarks>Reads the configuration from "Aspire:Azure:Storage:Queues:{name}" section.</remarks>
56-
/// <exception cref="InvalidOperationException">Thrown when neither <see cref="AzureStorageQueuesSettings.ConnectionString"/> nor <see cref="AzureStorageQueuesSettings.ServiceUri"/> is provided.</exception>
66+
/// <exception cref="InvalidOperationException">
67+
/// Neither <see cref="AzureStorageQueuesSettings.ConnectionString"/> nor <see cref="AzureStorageQueuesSettings.ServiceUri"/> is provided.
68+
/// </exception>
5769
public static void AddKeyedAzureQueueClient(
5870
this IHostApplicationBuilder builder,
5971
string name,
@@ -65,54 +77,4 @@ public static void AddKeyedAzureQueueClient(
6577

6678
new StorageQueueComponent().AddClient(builder, DefaultConfigSectionName, configureSettings, configureClientBuilder, connectionName: name, serviceKey: name);
6779
}
68-
69-
private sealed class StorageQueueComponent : AzureComponent<AzureStorageQueuesSettings, QueueServiceClient, QueueClientOptions>
70-
{
71-
protected override IAzureClientBuilder<QueueServiceClient, QueueClientOptions> AddClient(
72-
AzureClientFactoryBuilder azureFactoryBuilder, AzureStorageQueuesSettings settings, string connectionName,
73-
string configurationSectionName)
74-
{
75-
return ((IAzureClientFactoryBuilderWithCredential)azureFactoryBuilder).RegisterClientFactory<QueueServiceClient, QueueClientOptions>((options, cred) =>
76-
{
77-
var connectionString = settings.ConnectionString;
78-
if (string.IsNullOrEmpty(connectionString) && settings.ServiceUri is null)
79-
{
80-
throw new InvalidOperationException($"A QueueServiceClient could not be configured. Ensure valid connection information was provided in 'ConnectionStrings:{connectionName}' or specify a 'ConnectionString' or 'ServiceUri' in the '{configurationSectionName}' configuration section.");
81-
}
82-
83-
return !string.IsNullOrEmpty(connectionString)
84-
? new QueueServiceClient(connectionString, options)
85-
: cred is not null
86-
? new QueueServiceClient(settings.ServiceUri, cred, options)
87-
: new QueueServiceClient(settings.ServiceUri, options);
88-
}, requiresCredential: false);
89-
}
90-
91-
protected override void BindClientOptionsToConfiguration(IAzureClientBuilder<QueueServiceClient, QueueClientOptions> clientBuilder, IConfiguration configuration)
92-
{
93-
#pragma warning disable IDE0200 // Remove unnecessary lambda expression - needed so the ConfigBinder Source Generator works
94-
clientBuilder.ConfigureOptions(options => configuration.Bind(options));
95-
#pragma warning restore IDE0200
96-
}
97-
98-
protected override void BindSettingsToConfiguration(AzureStorageQueuesSettings settings, IConfiguration configuration)
99-
{
100-
configuration.Bind(settings);
101-
}
102-
103-
protected override IHealthCheck CreateHealthCheck(QueueServiceClient client, AzureStorageQueuesSettings settings)
104-
=> new AzureQueueStorageHealthCheck(client, new AzureQueueStorageHealthCheckOptions());
105-
106-
protected override bool GetHealthCheckEnabled(AzureStorageQueuesSettings settings)
107-
=> !settings.DisableHealthChecks;
108-
109-
protected override TokenCredential? GetTokenCredential(AzureStorageQueuesSettings settings)
110-
=> settings.Credential;
111-
112-
protected override bool GetMetricsEnabled(AzureStorageQueuesSettings settings)
113-
=> false;
114-
115-
protected override bool GetTracingEnabled(AzureStorageQueuesSettings settings)
116-
=> !settings.DisableTracing;
117-
}
11880
}

0 commit comments

Comments
 (0)