Abhängigkeitsinjektion mit dem Azure SDK für .NET

Dieser Artikel zeigt, wie Sie Azure-Dienstclients aus den neuesten Azure-Clientbibliotheken für .NET für die Injektion von Abhängigkeiten in einer .NET-Anwendung registrieren. Jede moderne .NET-Anwendung wird anhand der Anweisungen in einer Program.cs-Datei gestartet.

Installieren von Paketen

So registrieren und konfigurieren Sie Dienstclients aus einem Paket mit Präfix Azure.:

  1. Installieren Sie das Microsoft.Extensions.Azure-Paket in Ihrem Projekt:

    dotnet add package Microsoft.Extensions.Azure
    
  2. Installieren Sie das Azure.Identity-Paket, um einen TokenCredential-Typ für die Authentifizierung aller registrierten Clients zu konfigurieren, die einen solchen Typ akzeptieren:

    dotnet add package Azure.Identity
    

Zu Demonstrationszwecken verwendet der Beispielcode in diesem Artikel die Key Vault Secrets-, Blob Storage- und Service Bus-Bibliotheken. Installieren Sie die folgenden Pakete, um dem Beispiel zu folgen:

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

Registrieren von Clients und Unterclients

Ein Dienstclient ist der Einstiegspunkt für die API für einen Azure-Dienst – von dort aus können Bibliotheksbenutzer alle vom Dienst bereitgestellten Vorgänge aufrufen und die am häufigsten verwendeten Szenarien problemlos implementieren. Wo es das Design einer API vereinfacht, können Gruppen von Dienstaufrufen um kleinere Unterclienttypen organisiert werden. Beispielsweise kann ServiceBusClient zusätzliche ServiceBusSender-Unterclients zum Veröffentlichen von Nachrichten oder ServiceBusReceiver-Unterclients für die Verwendung von Nachrichten registrieren.

Rufen Sie in der Datei Program.cs die Erweiterungsmethode AddAzureClients auf, um einen Client für jeden Dienst zu registrieren. Die folgenden Codebeispiele enthalten Anleitungen zu Anwendungserstellern aus den Namespaces Microsoft.AspNetCore.Builder und Microsoft.Extensions.Hosting.

using Azure.Identity;
using Azure.Messaging.ServiceBus;
using Azure.Messaging.ServiceBus.Administration;
using Microsoft.Extensions.Azure;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

List<string> queueNames = await GetQueueNames();

builder.Services.AddAzureClients(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");
    clientBuilder.UseCredential(new DefaultAzureCredential());

    // Register a subclient for each Service Bus Queue
    foreach (string queue in queueNames)
    {
        clientBuilder.AddClient<ServiceBusSender, ServiceBusClientOptions>(
            (_, _, provider) => provider.GetService<ServiceBusClient>()
                    .CreateSender(queue)).WithName(queue);
    }
});

WebApplication app = builder.Build();

async Task<List<string>> GetQueueNames()
{
    // Query the available queues for the Service Bus namespace.
    var adminClient = new ServiceBusAdministrationClient
        ("<your_namespace>.servicebus.windows.net", new DefaultAzureCredential());
    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;
}

Für den Code oben gilt:

  • Key Vault Secrets- und Blob Storage- und Service Bus-Clients werden mit AddSecretClient, AddBlobServiceClient und AddServiceBusClientWithNamespace registriert. Die Uri- und string-Typargumente werden übergeben. Um zu vermeiden, dass Sie diese URLs explizit angeben, lesen Sie den Abschnitt Konfiguration getrennt vom Code speichern.
  • DefaultAzureCredential wird verwendet, um die Argumentanforderung TokenCredential für jeden registrierten Client zu erfüllen. Wenn einer der Clients erstellt wird, wird DefaultAzureCredential für die Authentifizierung verwendet.
  • Service Bus-Unterclients werden für jede Warteschlange im Dienst mithilfe des Unterclients und der entsprechenden Optionstypen registriert. Die Warteschlangennamen für die Unterclients werden mithilfe einer separaten Methode außerhalb der Dienstregistrierung abgerufen, da die GetQueuesAsync-Methode asynchron ausgeführt werden muss.

Verwenden der registrierten Clients

Wenn die Clients registriert sind, wie im Abschnitt Clients und Unterclients registrieren beschrieben, können Sie sie jetzt verwenden. Im folgenden Beispiel wird die Konstruktorinjektion verwendet, um den Blob Storage-Client in einem ASP.NET Core API-Controller abzurufen:

[ApiController]
[Route("[controller]")]
public class MyApiController : ControllerBase
{
    private readonly BlobServiceClient _blobServiceClient;
  
    public MyApiController(BlobServiceClient blobServiceClient)
    {
        _blobServiceClient = blobServiceClient;
    }
  
    [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();
    }
}

Speichern der Konfiguration getrennt vom Code

Im Abschnitt Clients und Unterclients registrieren haben Sie die Uri-Typvariablen explizit an die Clientkonstruktoren übergeben. Dieser Ansatz kann Probleme verursachen, wenn Sie während der Entwicklung und Produktion Code für verschiedene Umgebungen ausführen. Das .NET-Team empfiehlt, solche Konfigurationen in umgebungsabhängigen JSON-Dateien zu speichern. Beispielsweise können Sie eine Datei namens appsettings.Development.json anlegen, die Einstellungen für die Entwicklungsumgebung enthält. Eine weitere Datei namens appsettings.Production.json würde Einstellungen für die Produktionsumgebung enthalten. Das Dateiformat ist:

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

Sie können der JSON-Datei beliebige Eigenschaften aus der ClientOptions-Klasse hinzufügen. Die Einstellungen in der JSON-Konfigurationsdatei können mit IConfiguration abgerufen werden.

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

Im vorherigen JSON-Beispiel:

Konfigurieren mehrerer Dienstclients mit unterschiedlichen Namen

Angenommen, Sie verfügen über zwei Speicherkonten: eines für private Informationen und eines für öffentliche Informationen. Ihre App überträgt Daten nach einem Vorgang vom öffentlichen in das private Speicherkonto. Sie benötigen zwei Speicherdienstclients. Verwenden Sie die Erweiterungsmethode WithName, um diese beiden Clients zu unterscheiden:

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

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

Greifen Sie beispielsweise mithilfe eines ASP.NET Core-Controllers über die Schnittstelle IAzureClientFactory<TClient> auf den benannten Dienstclient zu:

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

Der unbenannte Dienstclient ist weiterhin auf die gleiche Weise wie zuvor verfügbar. Benannte Clients sind additiv.

Konfigurieren einer neuen Wiederholungsrichtlinie

Vielleicht möchten Sie irgendwann die Standardeinstellungen für einen Dienstclient ändern. Sie möchten z. B. andere Wiederholungseinstellungen oder eine andere Version der Service-API verwenden. Sie können die Wiederholungseinstellungen global oder dienstspezifisch festlegen. Angenommen, Sie haben die folgende Datei appsettings.json in Ihrem ASP.NET Core-Projekt:

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

Sie können die Wiederholungsrichtlinie wie folgt an Ihre Bedürfnisse anpassen:

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

Sie können Richtlinienüberschreibungen auch in der Datei appsettings.json speichern:

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

Siehe auch