Inserción de dependencias con Azure SDK para .NET
En este artículo se muestra cómo registrar clientes de servicio de Azure desde las bibliotecas de cliente de Azure para .NET más recientes para la inserción de dependencias en una aplicación .NET. Todas las aplicaciones .NET actuales se inician mediante las instrucciones que se proporcionan en el archivo Program.cs.
Instalar paquetes
Para registrar y configurar clientes de servicio desde un paquete con prefijo Azure.
:
Instale el paquete Microsoft.Extensions.Azure en su proyecto:
dotnet add package Microsoft.Extensions.Azure
Instale el paquete Azure.Identity para configurar un tipo
TokenCredential
que se usará para autenticar todos los clientes registrados que acepten este tipo:dotnet add package Azure.Identity
Con fines de demostración, el código de ejemplo de este artículo usa los secretos de Key Vault, Blob Storage, Service Bus y las bibliotecas de Azure OpenAI. Instale los siguientes paquetes para continuar:
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
Registro de clientes y subclientes
Un cliente de servicio es el punto de entrada de la API para un servicio de Azure. Desde este, los usuarios de biblioteca pueden invocar todas las operaciones que proporciona el servicio y pueden implementar fácilmente los escenarios más comunes. Al simplificar el diseño de una API, se pueden organizar los grupos de llamadas de servicio en torno a tipos de subclientes más pequeños. Por ejemplo, ServiceBusClient
puede registrar subclientes ServiceBusSender
adicionales para publicar mensajes, o subclientes ServiceBusReceiver
para consumir mensajes.
En el archivo Program.cs , invoque el método de extensión AddAzureClients para registrar un cliente para cada servicio. Los ejemplos de código siguientes proporcionan instrucciones sobre los generadores de aplicaciones de los espacios de nombres Microsoft.AspNetCore.Builder
y 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;
}
En el código anterior:
- Los secretos de Key Vault, Blob Storage y los clientes de Service Bus se registran mediante AddSecretClient, AddBlobServiceClient y AddServiceBusClientWithNamespace, respectivamente. Se pasan los argumentos con tipo
Uri
ystring
. Para evitar tener que especificar las direcciones URL explícitamente, consulte la sección Almacenamiento de la configuración por separado del código. - DefaultAzureCredential se usa para satisfacer el requisito de argumento
TokenCredential
para cada cliente registrado. Cuando se crea uno de los clientes,DefaultAzureCredential
se usa para autenticarse. - Los subclientes de Service Bus se registran para cada cola del servicio mediante el subcliente y los tipos de opciones correspondientes. Los nombres de cola de los subclientes se recuperan mediante un método independiente fuera del registro del servicio porque el método
GetQueuesAsync
se debe ejecutar de forma asincrónica. - Un cliente de Azure OpenAI se registra mediante un generador de cliente personalizado a través del AddClient método , que proporciona control sobre cómo se crea una instancia de cliente. Las factorías de cliente personalizadas son útiles en los casos siguientes:
- Debe usar otras dependencias durante la construcción del cliente.
- Un método de extensión de registro no existe para el cliente de servicio que desea registrar.
Uso de los clientes registrados
Con los clientes registrados, como se describe en la sección Registrar clientes y subclientes, ahora podrá usarlos. En el ejemplo siguiente, la inserción de constructores se usa para obtener el cliente de Blob Storage en un controlador de API de 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();
}
}
Almacenamiento de la configuración por separado del código
En la sección Registrar clientes y subclientes, ha pasado explícitamente las variables con tipo Uri
a los constructores de cliente. Este enfoque podría causar problemas al ejecutar el código en distintos entornos durante el desarrollo y la producción. El equipo de .NET sugiere almacenar estas configuraciones en archivos JSON dependientes del entorno. Por ejemplo, puede tener un archivo appsettings.Development.json que contenga la configuración del entorno de desarrollo. Otro archivo appsettings.Production.json contendrá la configuración del entorno de producción, y así sucesivamente. El formato de archivo es el siguiente:
{
"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"
}
}
Puede agregar cualquier propiedad de la clase ClientOptions al archivo JSON. La configuración del archivo de configuración JSON se puede recuperar mediante 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"));
});
En la muesta del código JSON anterior:
- Los nombres clave de nivel superior,
AzureDefaults
,KeyVault
,ServiceBus
yStorage
, son arbitrarios. Todos los demás nombres clave tienen significado y la serialización JSON se realiza de manera que no distingue mayúsculas de minúsculas. - Literal del objeto
AzureDefaults.Retry
:- Representa los valores de configuración de la directiva de reintento.
- Corresponde a la propiedad Retry. Dentro de ese literal de objeto, se encuentra la clave
MaxRetries
, que corresponde a la propiedad MaxRetries.
- Los valores de clave
KeyVault:VaultUri
,ServiceBus:Namespace
yStorage:ServiceUri
se asignan a los argumentos con tipoUri
ystring
de las sobrecargas de constructor Azure.Security.KeyVault.Secrets.SecretClient.SecretClient(Uri, TokenCredential, SecretClientOptions), Azure.Messaging.ServiceBus.ServiceBusClient.ServiceBusClient(String) y Azure.Storage.Blobs.BlobServiceClient.BlobServiceClient(Uri, TokenCredential, BlobClientOptions), respectivamente. Las variantes de los constructoresTokenCredential
se usan porque se establece un valor predeterminadoTokenCredential
a través de la llamada al método Microsoft.Extensions.Azure.AzureClientFactoryBuilder.UseCredential(TokenCredential).
Configuración de varios clientes de servicio con nombres diferentes
Imagine que tiene dos cuentas de almacenamiento: una para la información privada y otra para la pública. La aplicación transfiere datos de la cuenta de almacenamiento pública a la privada después de alguna operación. Debe tener dos clientes de servicio de almacenamiento. Para diferenciar esos dos clientes, use el método de extensión WithName:
builder.Services.AddAzureClients(clientBuilder =>
{
clientBuilder.AddBlobServiceClient(
builder.Configuration.GetSection("PublicStorage"));
clientBuilder.AddBlobServiceClient(
builder.Configuration.GetSection("PrivateStorage"))
.WithName("PrivateStorage");
});
Con un controlador de ASP.NET Core como ejemplo, acceda al cliente de servicio con nombre mediante la interfaz 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");
}
}
El cliente de servicio sin nombre sigue estando disponible igual que antes. Los clientes con nombre son acumulables.
Configuración de una nueva directiva de reintentos
En algún momento, es posible que quiera cambiar la configuración predeterminada de un cliente de servicio. Por ejemplo, puede que le interese una configuración de reintentos diferente o que prefiera usar una versión de API de servicio diferente. Puede establecer la configuración de reintentos globalmente o por servicio. Supongamos que tiene el siguiente archivo appsettings.json en el proyecto de 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"
}
}
Puede cambiar la directiva de reintentos para que cubra tus necesidades:
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);
});
});
También puede colocar invalidaciones de la directiva de reintentos en el archivo appsettings.json:
{
"KeyVault": {
"VaultUri": "https://mykeyvault.vault.azure.net",
"Retry": {
"maxRetries": 10
}
}
}