Поделиться через


Внедрение зависимостей с помощью пакета Azure SDK для .NET

В этой статье показано, как зарегистрировать клиенты службы Azure из последних клиентских библиотек Azure для .NET для внедрения зависимостей в приложении .NET. Каждое современное приложение .NET запускается с помощью инструкций, приведенных в файле Program.cs .

Установка пакетов

Чтобы зарегистрировать и настроить клиенты служб из Azure.префиксированного пакета:

  1. Установите пакет Microsoft.Extensions.Azure в проекте:

    dotnet add package Microsoft.Extensions.Azure
    
  2. Установите пакет Azure.Identity, чтобы настроить TokenCredential тип для проверки подлинности всех зарегистрированных клиентов, которые принимают такой тип:

    dotnet add package Azure.Identity
    

В демонстрационных целях пример кода в этой статье использует библиотеки секретов Key Vault, хранилища BLOB-объектов, служебной шины Service Bus и OpenAI. Установите следующие пакеты, чтобы выполнить следующие действия:

dotnet add package Azure.Security.KeyVault.Secrets
dotnet add package Azure.Storage.Blobs
dotnet add package Azure.Messaging.ServiceBus
dotnet add package OpenAI

Регистрация клиентов и подклиентов

Клиент службы — это точка входа в API для службы Azure— из нее пользователи библиотеки могут вызывать все операции, которые предоставляет служба и легко реализовать наиболее распространенные сценарии. Где это упрощает проектирование API, группы вызовов служб можно упорядочить вокруг небольших подклиентных типов. Например, ServiceBusClient можно зарегистрировать дополнительные ServiceBusSender подклиенты для публикации сообщений или ServiceBusReceiver подклиентов для использования сообщений.

В файле Program.cs вызовите AddAzureClients метод расширения для регистрации клиента для каждой службы. В следующих примерах кода приводятся рекомендации по построителям приложений из Microsoft.AspNetCore.Builder пространств имен и Microsoft.Extensions.Hosting пространств имен.

using Azure.Identity;
using Azure.Messaging.ServiceBus;
using Azure.Messaging.ServiceBus.Administration;
using Microsoft.Extensions.Azure;
using OpenAI;
using OpenAI.Responses;
using System.ClientModel.Primitives;

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");
    
    // AddAzureClients implicitly creates a DefaultAzureCredential instance
    // Create a credential manually to override the type or access it explicitly for DI registrations
    // This example shows credential reuse for GetQueueNames and AddClient calls downstream
    DefaultAzureCredential credential = new();
    clientBuilder.UseCredential(credential);

    // Register a subclient for each Service Bus Queue
    List<string> queueNames = await GetQueueNames(credential);
    foreach (string queueName in queueNames)
    {
        clientBuilder.AddClient<ServiceBusSender, ServiceBusClientOptions>((_, _, provider) =>
            provider.GetService(typeof(ServiceBusClient)) switch
            {
                ServiceBusClient client => client.CreateSender(queueName),
                _ => throw new InvalidOperationException("Unable to create ServiceBusClient")
            }).WithName(queueName);
    }

    var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
        ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is required.");

    // Register a custom client factory
    #pragma warning disable OPENAI001 // Type is for evaluation purposes and is subject to change in future updates.
    clientBuilder.AddClient<ResponsesClient, OpenAIClientOptions>(
        (options, credential, _) => new ResponsesClient(
            "<deployment_name>",
            new BearerTokenPolicy(credential, "https://ai.azure.com/.default"),
            new OpenAIClientOptions { Endpoint = new Uri($"{endpoint}/openai/v1/") }
        ));
    #pragma warning restore OPENAI001
});

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

В предыдущем коде:

  • Секреты Key Vault, хранилище BLOB-объектов и служебная шина клиенты регистрируются с помощью AddSecretClientAddBlobServiceClient и AddServiceBusClientWithNamespaceсоответственно. Передаются Uriаргументы - и stringтипизированные. Чтобы избежать явного указания этих URL-адресов, ознакомьтесь с конфигурацией Магазина отдельно от раздела кода .
  • Каждый зарегистрированный клиент автоматически использует DefaultAzureCredential для TokenCredential, если вы не настроите другой тип учетных данных (например, с помощью WithCredential).
  • служебная шина подклиенты регистрируются для каждой очереди в службе с помощью подклиента и соответствующих типов параметров. Имена очередей для подклиентов извлекаются с помощью отдельного метода вне регистрации службы, так как GetQueuesAsync метод должен выполняться асинхронно.
  • Регистрируется ResponsesClient посредством настраиваемой фабрики клиентов через метод AddClient, который обеспечивает контроль над созданием экземпляра клиента. Пользовательские клиентские фабрики полезны в следующих случаях:
    • Во время построения клиента необходимо использовать другие зависимости.
    • Метод расширения регистрации не существует для клиента службы, который требуется зарегистрировать.

Использование зарегистрированных клиентов

Зарегистрированные клиенты, как описано в разделе "Регистрация клиентов и подклиентов ", теперь можно использовать их. В следующем примере внедрение конструктора используется для получения клиента хранилища BLOB-объектов и фабрики для подклиентов отправителя служебная шина в контроллере API ядра ASP.NET:

[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();
    }
}

Хранение конфигурации отдельно от кода

В разделе "Регистрация клиентов и подклиентов" вы явно передали Uriтипизированные переменные конструкторам клиента. Такой подход может вызвать проблемы при выполнении кода в различных средах при разработке и в работе. Команда разработчиков .NET предлагает хранить такие конфигурации в отдельных JSON-файлах для соответствующих сред. Например, в файле appsettings.Development.js можно хранить параметры среды разработки. В другом файле, appsettings.Production.js, храните параметры рабочей среды и т. д. У файлов должен быть следующий формат.

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

В JSON-файл можно добавить любые свойства из ClientOptions класса. Параметры в файле конфигурации JSON можно получить с помощью IConfiguration.

builder.Services.AddAzureClients(clientBuilder =>
{
    clientBuilder.AddSecretClient(
        builder.Configuration.GetSection("KeyVault"));

    clientBuilder.AddBlobServiceClient(
        builder.Configuration.GetSection("Storage"));

    clientBuilder.AddServiceBusClientWithNamespace(
        builder.Configuration["ServiceBus:Namespace"]);

    // Set up any default settings
    clientBuilder.ConfigureDefaults(
        builder.Configuration.GetSection("AzureDefaults"));
});

В предыдущем примере JSON:

Настройка разных имен для нескольких клиентов служб

Представьте, что у вас есть две учетные записи хранения: одна для частной информации и другая для общедоступной информации. Приложение передает данные из общедоступной учетной записи хранения в частную учетную запись хранения после некоторой операции. Необходимо два клиента служб хранения. Чтобы отличить эти два клиента, используйте WithName метод расширения:

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

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

С помощью контроллера ASP.NET Core в качестве примера можно получить доступ к именованный клиент службы с помощью 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");
    }
}

Неименованные клиенты служб будут доступны, как и прежде. Именованные клиенты аддитивны.

Настройка новой политики повтора

В какой-то момент может потребоваться изменить параметры по умолчанию для клиента службы. Например, могут потребоваться разные параметры повторных попыток или использовать другую версию API службы. Вы можете задавать параметры повтора глобально или же отдельно для каждой службы. Предположим, что в проекте ASP.NET Core есть следующий файл appsettings.json :

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

Вы можете изменить политику повторных попыток в соответствии с вашими потребностями следующим образом:

builder.Services.AddAzureClients(clientBuilder =>
{
    // Establish the global defaults
    clientBuilder.ConfigureDefaults(
        builder.Configuration.GetSection("AzureDefaults"));

    // 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);
        });
});

Вы также можете поместить переопределения политики повторных попыток в файл appsettings.json :

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

См. также