Injeção de dependência com o SDK do Azure para .NET
Este artigo demonstra como registrar clientes de serviço do Azure dos SDKs do Azure mais recentes para .NET para injeção de dependência em um aplicativo .NET. Todo aplicativo .NET moderno é inicializado usando as instruções fornecidas em um arquivo Program.cs.
Instalar Pacotes
Para registrar e configurar clientes de serviço de um Azure.
-pacote prefixado:
Instale o pacote Microsoft.Extensions.Azure em seu projeto:
dotnet add package Microsoft.Extensions.Azure
Instale o pacote Azure.Identity para configurar um tipo
TokenCredential
a ser usado para autenticar todos os clientes registrados que aceitam esse tipo:dotnet add package Azure.Identity
Para fins de demonstração, o código de exemplo neste artigo usa as bibliotecas Key Vault Secrets, Blob Storage, Service Bus e Azure OpenAI. Instale os seguintes pacotes para acompanhar:
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
Registrar clientes e subclientes
Um cliente de serviço é o ponto de entrada para a API de um serviço do Azure. A partir dele, os usuários da biblioteca podem invocar todas as operações que o serviço fornece e implementar facilmente os cenários mais comuns. Em locais onde ele simplificará o design de uma API, grupos de chamadas de serviço podem ser organizados em torno de tipos de subclientes menores. Por exemplo, ServiceBusClient
pode registrar mais ServiceBusSender
subclientes para publicar mensagens ou ServiceBusReceiver
subclientes para consumir mensagens.
No arquivo Program.cs, invoque o método de extensão AddAzureClients para registrar um cliente para cada serviço. Os exemplos de código a seguir fornecem diretrizes sobre construtores de aplicativos dos namespaces Microsoft.AspNetCore.Builder
e 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;
}
No código anterior:
- Os clientes dos Segredos do Key Vault, do Armazenamento de Blobs e do Barramento de Serviço são registrados usando o AddSecretClient, AddBlobServiceClient e AddServiceBusClientWithNamespace, respectivamente. Os argumentos de tipo
Uri
estring
são passados. Para evitar especificar essas URLs explicitamente, confira a seção Armazenar a configuração separadamente do código. - DefaultAzureCredential é usado para atender ao requisito do argumento
TokenCredential
para cada cliente registrado. Quando um dos clientes é criado,DefaultAzureCredential
é usado para autenticar. - Os subclientes do Barramento de Serviço são registrados para cada fila no serviço usando o subcliente e os tipos de opções correspondentes. Os nomes de fila para os subclientes são recuperados usando um método separado fora do registro de serviço porque o método
GetQueuesAsync
deve ser executado de forma assíncrona. - Um cliente OpenAI do Azure é registrado usando uma fábrica de clientes personalizada por meio do AddClient método, que fornece controle sobre como uma instância de cliente é criada. As fábricas de clientes personalizadas são úteis nos seguintes casos:
- Você precisa usar outras dependências durante a construção do cliente.
- Não existe um método de extensão de registro para o cliente de serviço que você deseja registrar.
Usar os clientes registrados
Com os clientes registrados, conforme descrito na seção Registrar clientes e subclientes, agora você pode usá-los. No exemplo a seguir, a injeção de construção é usada para obter o cliente de Armazenamento de Blobs e uma fábrica para obter subclientes do remetente do Barramento de Serviço em um controlador de API do 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();
}
}
Armazenar configuração separadamente do código
Na seção Registrar clientes e subclientes, você passou explicitamente as variáveis de tipo Uri
para os construtores do cliente. Essa abordagem pode causar problemas ao executar o código em diferentes ambientes durante o desenvolvimento e a produção. A equipe .NET sugere armazenar essas configurações em arquivos JSON dependentes do ambiente . Por exemplo, você pode ter um arquivo appsettings.Development.json contendo as configurações do ambiente de desenvolvimento. Outro arquivo appsettings.Production.json conteria as configurações do ambiente de produção e assim por diante. O formato do arquivo é:
{
"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"
}
}
Você pode adicionar todas as propriedades da classe ClientOptions ao arquivo JSON. As configurações no arquivo de configuração JSON podem ser recuperadas usando 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"));
});
No exemplo JSON anterior:
- Os nomes de chave de nível superior,
AzureDefaults
,KeyVault
,ServiceBus
eStorage
, são arbitrários. Todos os outros nomes de chave têm significado e a serialização JSON é executada de maneira que não diferencia maiúsculas de minúsculas. - O literal do objeto
AzureDefaults.Retry
:- Representa as definições de configuração da política de repetição.
- Corresponde à propriedade Retry. Dentro desse literal de objeto, você encontra a chave
MaxRetries
, que corresponde à propriedade MaxRetries.
- Os valores de chave
KeyVault:VaultUri
,ServiceBus:Namespace
eStorage:ServiceUri
são mapeados para os argumentos de tipoUri
estring
das sobrecargas do construtor Azure.Security.KeyVault.Secrets.SecretClient.SecretClient(Uri, TokenCredential, SecretClientOptions), Azure.Messaging.ServiceBus.ServiceBusClient.ServiceBusClient(String) e Azure.Storage.Blobs.BlobServiceClient.BlobServiceClient(Uri, TokenCredential, BlobClientOptions), respectivamente. As variantesTokenCredential
dos construtores são usadas porque um padrãoTokenCredential
é definido por meio da chamada do método Microsoft.Extensions.Azure.AzureClientFactoryBuilder.UseCredential(TokenCredential).
Configurar vários clientes de serviço com nomes diferentes
Imagine que você possui duas contas de armazenamento: uma para informações privadas e outra para informações públicas. Seu aplicativo transfere dados da conta de armazenamento pública para a privada após alguma operação. Você precisa ter dois clientes de serviço de armazenamento. Para diferenciar esses dois clientes, use o método de extensão WithName:
builder.Services.AddAzureClients(clientBuilder =>
{
clientBuilder.AddBlobServiceClient(
builder.Configuration.GetSection("PublicStorage"));
clientBuilder.AddBlobServiceClient(
builder.Configuration.GetSection("PrivateStorage"))
.WithName("PrivateStorage");
});
Usando um controlador de ASP.NET Core como exemplo, acesse o cliente de serviço nomeado usando a 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");
}
}
O cliente de serviço sem nome ainda está disponível da mesma maneira que antes. Os clientes nomeados são aditivos.
Configurar uma nova política de repetição
Em algum momento, talvez você deseje alterar as configurações padrão de um cliente de serviço. Por exemplo, talvez você queira usar configurações de repetição diferentes ou usar uma versão de API de serviço diferente. Você pode definir as configurações de repetição globalmente ou por serviço. Suponha que você tenha o seguinte arquivo appsettings.json em seu projeto 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"
}
}
Você pode alterar a política de repetição para atender às suas necessidades da seguinte forma:
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);
});
});
Você também pode adicionar substituições de política de repetição no arquivo appsettings.json:
{
"KeyVault": {
"VaultUri": "https://mykeyvault.vault.azure.net",
"Retry": {
"maxRetries": 10
}
}
}