Injeção de dependência com o SDK do Azure para .NET

Este artigo demonstra como registrar clientes de serviço do Azure das bibliotecas de cliente mais recentes do Azure para .NET para injeção de dependência em um aplicativo .NET. Cada aplicativo .NET moderno é iniciado usando as instruções fornecidas em um arquivo Program.cs .

Instalar pacotes

Para registrar e configurar clientes de serviço a partir de um Azure.pacote prefixado:

  1. Instale o pacote Microsoft.Extensions.Azure em seu projeto:

    dotnet add package Microsoft.Extensions.Azure
    
  2. Instale o pacote Azure.Identity para configurar um TokenCredential tipo 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

Registar 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 podem implementar facilmente os cenários mais comuns. Onde 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 subclientes adicionais ServiceBusSender para publicar mensagens ou ServiceBusReceiver subclientes para consumir mensagens.

No arquivo Program.cs, invoque o AddAzureClients método extension para registrar um cliente para cada serviço. Os exemplos de código a seguir fornecem orientação sobre construtores de aplicativos dos Microsoft.AspNetCore.Builder namespaces 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 Key Vault Secrets, Blob Storage e Service Bus são registrados usando o AddSecretClient, AddBlobServiceClient e AddServiceBusClientWithNamespace, respectivamente. Os Uriargumentos - e string-typed são passados. Para evitar especificar esses URLs explicitamente, consulte a seção Configuração da loja separadamente do código .
  • DefaultAzureCredential é usado para satisfazer o requisito de TokenCredential argumento 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 GetQueuesAsync método deve ser executado de forma assíncrona.
  • Um cliente OpenAI do Azure é registrado usando uma fábrica de cliente personalizada por meio do AddClient método, que fornece controle sobre como uma instância de cliente é criada. As fábricas de clientes personalizados 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.

Utilize os clientes registados

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 do construtor é usada para obter o cliente de armazenamento de Blob e uma fábrica para subclientes de remetente do Service Bus em um controlador de 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();
    }
}

Armazene a configuração separadamente do código

Na seção Registrar clientes e subclientes, você passou explicitamente as Urivariáveis -typed para os construtores de cliente. Essa abordagem pode causar problemas quando você executa código em ambientes diferentes durante o desenvolvimento e a produção. A equipe do .NET sugere armazenar essas configurações em arquivos JSON dependentes do ambiente. Por exemplo, você pode ter um appsettings. Development.json arquivo contendo as configurações do ambiente de desenvolvimento. Outra appsettings. Production.json arquivo conteria configurações de ambiente de produção e assim por diante. O formato do ficheiro é:

{
  "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 quaisquer propriedades da ClientOptions classe no 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:

Configurar vários clientes de serviço com nomes diferentes

Imagine que você tem 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 WithName método de extensão:

builder.Services.AddAzureClients(clientBuilder =>
{
    clientBuilder.AddBlobServiceClient(
        builder.Configuration.GetSection("PublicStorage"));

    clientBuilder.AddBlobServiceClient(
            builder.Configuration.GetSection("PrivateStorage"))
        .WithName("PrivateStorage");
});

Usando um controlador ASP.NET Core como exemplo, acesse o cliente de serviço nomeado usando a IAzureClientFactory<TClient> interface:

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 forma que antes. Os clientes nomeados são aditivos.

Configurar uma nova política de repetição

Em algum momento, talvez você queira alterar as configurações padrão de um cliente de serviço. Por exemplo, você pode querer configurações de repetição diferentes ou usar uma versão diferente da API de serviço. Você pode definir as configurações de repetição globalmente ou por serviço. Suponha que você tenha o seguinte arquivo de 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 colocar substituições de política de repetição no arquivo appsettings.json :

{
  "KeyVault": {
    "VaultUri": "https://mykeyvault.vault.azure.net",
    "Retry": {
      "maxRetries": 10
    }
  }
}

Consulte também