Compartir a través de


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.:

  1. Instale el paquete Microsoft.Extensions.Azure en su proyecto:

    dotnet add package Microsoft.Extensions.Azure
    
  2. 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 y string. 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:

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
    }
  }
}

Consulte también