Injection de dépendances avec le Kit de développement logiciel (SDK) Azure pour .NET
Cet article explique comment inscrire des clients de service Azure à partir des dernières bibliothèques clientes Azure pour .NET pour l’injection de dépendances dans une application .NET. Chaque application .NET moderne démarre à l’aide des instructions fournies dans un fichier Program.cs.
Installer des packages
Pour inscrire et configurer des clients de service à partir d’un package avec préfixe Azure.
:
Installez le package Microsoft.Extensions.Azure dans votre projet :
dotnet add package Microsoft.Extensions.Azure
Installez le package Azure.Identity pour configurer un type
TokenCredential
à utiliser pour authentifier tous les clients inscrits qui acceptent ce type :dotnet add package Azure.Identity
À des fins de démonstration, l’exemple de code de cet article utilise les bibliothèques Key Vault Secrets, Blob Storage, Service Bus et Azure OpenAI. Installez ensuite les packages suivants :
dotnet add package Azure.Security.KeyVault.Secrets
dotnet add package Azure.Storage.Blobs
dotnet add package Azure.Messaging.ServiceBus
dotnet add package Azure.AI.OpenAI
Inscrire des clients et des sous-clients
Un client de service est le point d’entrée de l’API d’un service Azure. À partir de celui-ci, les utilisateurs de bibliothèque peuvent appeler toutes les opérations que le service fournit et peuvent facilement implémenter les scénarios les plus courants. Pour simplifier la conception d’une API, les groupes d’appels de service peuvent être organisés en types de sous-clients plus petits. Par exemple, ServiceBusClient
peut inscrire des sous-clients ServiceBusSender
supplémentaires pour la publication des messages, ou des sous-clients ServiceBusReceiver
pour la consommation des messages.
Dans le fichier Program.cs, appelez la méthode d’extension AddAzureClients pour inscrire un client pour chaque service. Les exemples de code suivants fournissent une aide sur les générateurs d’applications à partir des espaces de noms Microsoft.AspNetCore.Builder
et Microsoft.Extensions.Hosting
.
using Azure.Identity;
using Azure.Messaging.ServiceBus;
using Azure.Messaging.ServiceBus.Administration;
using Microsoft.Extensions.Azure;
using Azure.AI.OpenAI;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddAzureClients(async clientBuilder =>
{
// Register clients for each service
clientBuilder.AddSecretClient(new Uri("<key_vault_url>"));
clientBuilder.AddBlobServiceClient(new Uri("<storage_url>"));
clientBuilder.AddServiceBusClientWithNamespace(
"<your_namespace>.servicebus.windows.net");
// Set a credential for all clients to use by default
DefaultAzureCredential credential = new();
clientBuilder.UseCredential(credential);
// Register a subclient for each Service Bus Queue
List<string> queueNames = await GetQueueNames(credential);
foreach (string queue in queueNames)
{
clientBuilder.AddClient<ServiceBusSender, ServiceBusClientOptions>(
(_, _, provider) => provider.GetService<ServiceBusClient>()
.CreateSender(queue)).WithName(queue);
}
// Register a custom client factory
clientBuilder.AddClient<AzureOpenAIClient, AzureOpenAIClientOptions>(
(options, _, _) => new AzureOpenAIClient(
new Uri("<url_here>"), credential, options));
});
WebApplication app = builder.Build();
async Task<List<string>> GetQueueNames(DefaultAzureCredential credential)
{
// Query the available queues for the Service Bus namespace.
var adminClient = new ServiceBusAdministrationClient
("<your_namespace>.servicebus.windows.net", credential);
var queueNames = new List<string>();
// Because the result is async, the queue names need to be captured
// to a standard list to avoid async calls when registering. Failure to
// do so results in an error with the services collection.
await foreach (QueueProperties queue in adminClient.GetQueuesAsync())
{
queueNames.Add(queue.Name);
}
return queueNames;
}
Dans le code précédent :
- Les clients Secrets Key Vault, Stockage Blob et Service Bus sont inscrits à l’aide de AddSecretClient, AddBlobServiceClient et AddServiceBusClientWithNamespace, respectivement. Les arguments de type
Uri
etstring
sont passés. Pour éviter de spécifier explicitement ces URL, consultez la section Stocker la configuration séparément du code. - DefaultAzureCredential est utilisé pour satisfaire l’exigence d’argument
TokenCredential
pour chaque client inscrit. Quand l’un des clients est créé,DefaultAzureCredential
est utilisé pour l’authentification. - Les sous-clients de Service Bus sont inscrits pour chaque file d’attente du service à l’aide du sous-client et des types d’options correspondants. Les noms de files d’attente des sous-clients sont récupérés à l’aide d’une méthode distincte en dehors de l’inscription du service, car la méthode
GetQueuesAsync
doit être exécutée de manière asynchrone. - Un client Azure OpenAI est inscrit à l’aide d’une fabrique de client personnalisée via la AddClient méthode, qui permet de contrôler la façon dont une instance cliente est créée. Les fabriques clientes personnalisées sont utiles dans les cas suivants :
- Vous devez utiliser d’autres dépendances pendant la construction du client.
- Une méthode d’extension d’inscription n’existe pas pour le client de service que vous souhaitez inscrire.
Utiliser les clients inscrits
Une fois les clients inscrits, comme indiqué dans la section Inscrire des clients et des sous-clients, vous pouvez désormais les utiliser. Dans l’exemple suivant, l’injection de constructeurr est utilisée pour obtenir le client Blob Storage et une fabrique de sous-clients d’expéditeur de bus de services dans un contrôleur API ASP.NET Core :
[ApiController]
[Route("[controller]")]
public class MyApiController : ControllerBase
{
private readonly BlobServiceClient _blobServiceClient;
private readonly ServiceBusSender _serviceBusSender;
public MyApiController(
BlobServiceClient blobServiceClient,
IAzureClientFactory<ServiceBusSender> senderFactory)
{
_blobServiceClient = blobServiceClient;
_serviceBusSender = senderFactory.CreateClient("myQueueName");
}
[HttpGet]
public async Task<IEnumerable<string>> Get()
{
BlobContainerClient containerClient =
_blobServiceClient.GetBlobContainerClient("demo");
var results = new List<string>();
await foreach (BlobItem blob in containerClient.GetBlobsAsync())
{
results.Add(blob.Name);
}
return results.ToArray();
}
}
Stocker la configuration séparément du code
Dans la section Inscrire des clients et des sous-clients, vous avez passé explicitement les variables de type Uri
aux constructeurs clients. Cette approche peut entraîner des problèmes lorsque vous exécutez du code sur différents environnements pendant le développement et la production. L’équipe .NET suggère de stocker ces configurations dans des fichiers JSON dépendant de l’environnement. Par exemple, vous pouvez avoir un fichier appsettings.Development.json contenant les paramètres d’environnement de développement. Un autre fichier appsettings.Production.json contient des paramètres d’environnement de production, etc. Le format de fichier est :
{
"AzureDefaults": {
"Diagnostics": {
"IsTelemetryDisabled": false,
"IsLoggingContentEnabled": true
},
"Retry": {
"MaxRetries": 3,
"Mode": "Exponential"
}
},
"KeyVault": {
"VaultUri": "https://mykeyvault.vault.azure.net"
},
"ServiceBus": {
"Namespace": "<your_namespace>.servicebus.windows.net"
},
"Storage": {
"ServiceUri": "https://mydemoaccount.storage.windows.net"
}
}
Vous pouvez ajouter des propriétés de la classe ClientOptions dans le fichier JSON. Les paramètres du fichier de configuration JSON peuvent être récupérés à l’aide de IConfiguration.
builder.Services.AddAzureClients(clientBuilder =>
{
clientBuilder.AddSecretClient(
builder.Configuration.GetSection("KeyVault"));
clientBuilder.AddBlobServiceClient(
builder.Configuration.GetSection("Storage"));
clientBuilder.AddServiceBusClientWithNamespace(
builder.Configuration["ServiceBus:Namespace"]);
clientBuilder.UseCredential(new DefaultAzureCredential());
// Set up any default settings
clientBuilder.ConfigureDefaults(
builder.Configuration.GetSection("AzureDefaults"));
});
Dans l’exemple JSON précédent :
- Les noms de clés de premier niveau,
AzureDefaults
,KeyVault
,ServiceBus
etStorage
, sont arbitraires. Tous les autres noms de clé sont significatifs, et la sérialisation JSON est effectuée de manière non sensible à la casse. - Littéral d’objet
AzureDefaults.Retry
:- Représente les paramètres de configuration de la stratégie de nouvelles tentatives.
- Correspond à la propriété Retry. Dans ce littéral d’objet, vous trouverez la clé
MaxRetries
, qui correspond à la propriété MaxRetries.
- Les valeurs de clé
KeyVault:VaultUri
,ServiceBus:Namespace
etStorage:ServiceUri
sont mappées aux arguments de typeUri
etstring
des surcharges de constructeur Azure.Security.KeyVault.Secrets.SecretClient.SecretClient(Uri, TokenCredential, SecretClientOptions), Azure.Messaging.ServiceBus.ServiceBusClient.ServiceBusClient(String) et Azure.Storage.Blobs.BlobServiceClient.BlobServiceClient(Uri, TokenCredential, BlobClientOptions), respectivement. Les variantesTokenCredential
des constructeurs sont utilisées, car une valeur par défautTokenCredential
est définie via l’appel de méthode Microsoft.Extensions.Azure.AzureClientFactoryBuilder.UseCredential(TokenCredential).
Configurer plusieurs clients de service avec des noms différents
Supposons que vous disposez de deux comptes de stockage : un pour les informations privées et un autre pour les informations publiques. Votre application transfère des données du compte de stockage public vers le privé après une opération. Vous devez avoir deux clients de service de stockage. Pour différencier ces deux clients, utilisez la méthode d’extension WithName :
builder.Services.AddAzureClients(clientBuilder =>
{
clientBuilder.AddBlobServiceClient(
builder.Configuration.GetSection("PublicStorage"));
clientBuilder.AddBlobServiceClient(
builder.Configuration.GetSection("PrivateStorage"))
.WithName("PrivateStorage");
});
En utilisant un contrôleur ASP.NET Core comme exemple, accédez au client de service nommé à l’aide de l’interface IAzureClientFactory<TClient> :
public class HomeController : Controller
{
private readonly BlobServiceClient _publicStorage;
private readonly BlobServiceClient _privateStorage;
public HomeController(
BlobServiceClient defaultClient,
IAzureClientFactory<BlobServiceClient> clientFactory)
{
_publicStorage = defaultClient;
_privateStorage = clientFactory.CreateClient("PrivateStorage");
}
}
Le client de service sans nom est toujours disponible, de la même manière qu’auparavant. Les clients nommés sont additifs.
Configurer une nouvelle stratégie de nouvelles tentatives
À un moment donné, vous pouvez modifier les paramètres par défaut d’un client de service. Par exemple, vous pouvez souhaiter des paramètres de nouvelles tentatives différents ou utiliser une autre version de l’API de service. Vous pouvez définir les paramètres de nouvelles tentatives globalement ou par service. Supposons que vous disposez du fichier appsettings.json suivant dans votre projet ASP.NET Core :
{
"AzureDefaults": {
"Retry": {
"maxRetries": 3
}
},
"KeyVault": {
"VaultUri": "https://mykeyvault.vault.azure.net"
},
"ServiceBus": {
"Namespace": "<your_namespace>.servicebus.windows.net"
},
"Storage": {
"ServiceUri": "https://store1.storage.windows.net"
},
"CustomStorage": {
"ServiceUri": "https://store2.storage.windows.net"
}
}
Vous pouvez modifier la stratégie de nouvelles tentatives en fonction de vos besoins, comme suit :
builder.Services.AddAzureClients(clientBuilder =>
{
// Establish the global defaults
clientBuilder.ConfigureDefaults(
builder.Configuration.GetSection("AzureDefaults"));
clientBuilder.UseCredential(new DefaultAzureCredential());
// A Key Vault Secrets client using the global defaults
clientBuilder.AddSecretClient(
builder.Configuration.GetSection("KeyVault"));
// A Blob Storage client with a custom retry policy
clientBuilder.AddBlobServiceClient(
builder.Configuration.GetSection("Storage"))
.ConfigureOptions(options => options.Retry.MaxRetries = 10);
clientBuilder.AddServiceBusClientWithNamespace(
builder.Configuration["ServiceBus:Namespace"])
.ConfigureOptions(options => options.RetryOptions.MaxRetries = 10);
// A named storage client with a different custom retry policy
clientBuilder.AddBlobServiceClient(
builder.Configuration.GetSection("CustomStorage"))
.WithName("CustomStorage")
.ConfigureOptions(options =>
{
options.Retry.Mode = Azure.Core.RetryMode.Exponential;
options.Retry.MaxRetries = 5;
options.Retry.MaxDelay = TimeSpan.FromSeconds(120);
});
});
Vous pouvez également placer des remplacements de stratégie de nouvelles tentatives dans le fichier appsettings.json :
{
"KeyVault": {
"VaultUri": "https://mykeyvault.vault.azure.net",
"Retry": {
"maxRetries": 10
}
}
}