Using dependency injection with Azure .NET SDK
Tags: azure, dotnet, csharp
Published: 2024-02-19 19:56:09

---
title: Using dependency injection with Azure .NET SDK
published: true
date: 2024-02-19 00:00:00 UTC
tags: azure, dotnet, csharp
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nc0mny4xluprzvw56qug.jpg
canonical_url: https://techwatching.dev/posts/azure-sdk-di
---

I love how the Azure SDKs have evolved over the years. In the past, there was no consistency between the various Azure SDKs. However, that's not longer the case (at least for most Azure libraries), as they now adhere to the same principles and follow a set of well-defined [guidelines](https://azure.github.io/azure-sdk/general_introduction.html).

> 💡You can learn more about these guidelines and how the Azure .NET SDKs work in this [video](https://youtu.be/v36NXLU3TLY?si=L8e1ic898kDCisJ7) from 2021 which is I think still relevant today.

Having consistency between libraries, it's easier to handle things like authentication and dependency injection consistently when you are using multiple Azure SDKs in your project.

One aspect often overlooked by people using Azure SDKs is the use of [`Microsoft.Extensions.Azure`](https://www.nuget.org/packages/Microsoft.Extensions.Azure). This package facilitates registering and configuring the service clients for interacting with Azure APIs.

Let's see why using this package could be beneficial for your project.

## Avoid making mistakes when registering service clients

It's mentioned in the [documentation](https://learn.microsoft.com/en-us/dotnet/azure/sdk/dependency-injection?view=azure-dotnet&tabs=web-app-builder) to use this package for dependency injection with the Azure SDK for .NET. Still, many people don't read the documentation and manually register the Azure service clients.

It's not a problem in itself if you know what you are doing. Otherwise,

- you might choose the wrong lifetime for the Azure service clients, they must be singleton

- you may forget to register a dependency that is needed for your use of the SDK

```csharp
using Azure.Identity;
using DIWithAzureSDK;
using Microsoft.Extensions.Azure;

var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
builder.Services.AddAzureClients(clientBuilder =>
{
    clientBuilder.AddBlobServiceClient(new Uri("https://stdiwithazuresdk.blob.core.windows.net/"));
    clientBuilder.UseCredential(new DefaultAzureCredential());
});

var host = builder.Build();
host.Run();

```

In this sample, the `AddBlobServiceClient` handles the registration of all dependencies for us so that the `BlobServiceClient` can then be injected directly where needed.

```csharp
public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    private readonly BlobServiceClient _blobServiceClient;

    public Worker(ILogger<Worker> logger, BlobServiceClient blobServiceClient)
    {
        _logger = logger;
        _blobServiceClient = blobServiceClient;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await foreach (var blobContainer in _blobServiceClient.GetBlobContainersAsync(cancellationToken: stoppingToken))
        {
            _logger.LogInformation(blobContainer.Name);
        }
    }
}

```

> 💬 You may find it convenient to configure the dependency injection for all Azure service clients in a central place with the `AddAzureClients` method. When applications become larger with different `csproj,` I often prefer to separate service registration by business domain/module so having everything in a central place does not always suit my needs. That's not a problem, as the internal methods of the library make use of the `TryAddd` methods for registering services, I can call `AddAzureClients` in multiple places with only the services I want to register.

## Easily manage the authentication to Azure services

All the SDKs use the [Azure.Identity](https://www.nuget.org/packages/Azure.Identity) package to authenticate to Azure. There are different authentication methods available and you can easily specify which one to use with each client. Additionally, you can define a default authentication method for all clients, as demonstrated in the previous example.

```csharp
builder.Services.AddAzureClients(clientBuilder =>
{
    clientBuilder.AddServiceBusClient("https://sb-diwithazuresdk.servicebus.windows.net/")
        .WithCredential(new ManagedIdentityCredential());
    
    clientBuilder.AddTableServiceClient(new Uri("https://stdiwithazuresdk.table.core.windows.net"))
        .WithCredential(new EnvironmentCredential());

    clientBuilder.AddBlobServiceClient(new Uri("https://stdiwithazuresdk.blob.core.windows.net/"));
    
    clientBuilder.UseCredential(new DefaultAzureCredential());
});

```

In the example above, we configured:

- the service bus client to use the managed identity of the application to obtain a valid token for the service bus

- the table client to use environment variables to obtain a valid token for the storage table

- the blob client without any credentials so that it will use the one that we configured by default (with the `UseCredential` method)

## Effortlessly configure the Azure clients' options

All Azure clients have options that can be effortlessly configured when registering them in the `AddAzureClients` method.

```csharp
builder.Services.AddAzureClients(clientBuilder =>
{
    clientBuilder.AddBlobServiceClient(new Uri("https://stdiwithazuresdk.blob.core.windows.net/"))
        .WithCredential(new DefaultAzureCredential())
        .ConfigureOptions(options =>
        {
            options.TrimBlobNameSlashes = true;
            options.Retry.MaxRetries = 10;
            options.Diagnostics.IsLoggingEnabled = false;
        });
});

```

Some options are specific to the client (like the `TrimBlobNameSlashes` here for Blob client). Others can be configured globally and overridden on a client if necessary.

```csharp
builder.Services.AddAzureClients(clientBuilder =>
{
   clientBuilder.AddBlobServiceClient(new Uri("https://stdiwithazuresdk.blob.core.windows.net/"))
        .WithCredential(new DefaultAzureCredential())
        .ConfigureOptions(options =>
        {
            options.TrimBlobNameSlashes = true;
            options.Retry.MaxRetries = 10;
            options.Diagnostics.IsLoggingEnabled = false;
        });
    
    clientBuilder.ConfigureDefaults(options =>
    {
        options.Retry.MaxRetries = 5;
        options.Retry.Mode = RetryMode.Exponential;
        options.Diagnostics.IsDistributedTracingEnabled = true;
    });
});

```

That's the purpose of the `ConfigureDefaults` method.

> 💡Please note that all this configuration (as well as the Uris of each client) can be loaded from the configuration like this `clientBuilder.AddTableServiceClient(builder.Configuration.GetSection("Inventory:Tables"));`

## Use named clients for different Azure resources

Usually, you only need one client of each SDK in your application. Let's say you have multiple Azure Storage tables that are used in your application, you will only need to have one `TableServiceClient`. However, if you are interacting with tables in two different storage accounts, you will need multiple table clients.

To do that you can register your clients with a specific name:

```csharp
builder.Services.AddAzureClients(clientBuilder =>
{
    clientBuilder.AddTableServiceClient(builder.Configuration.GetSection("Shop:Inventory"))
        .WithName("Shop");
    clientBuilder.AddTableServiceClient(builder.Configuration.GetSection("Warehouse:Inventory"))
        .WithName("Warehouse");
}

```

This way, you will be able to retrieve the specific client you need in your code:

```csharp
public class WarehouseDeliveryService
{
    private readonly TableServiceClient _tableServiceClient;

    public WarehouseDeliveryService(IAzureClientFactory<TableServiceClient> azureClientFactory)
    {
        _tableServiceClient = azureClientFactory.CreateClient("Warehouse");
    }
}

```

## Register a custom client factory

If you have [specific needs](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/extensions/Microsoft.Extensions.Azure/README.md#registering-a-custom-client-factory), the `AddClient` method can help you register your Azure client while letting you control how you instantiate the client.

For instance, the Azure Cosmos Db .NET SDK is not built on the same foundation (`Azure.Core`) as the other SDKs. So at the time of writing, there is no `AddCosmosServiceClient` you can use in the `AddAzureClients` (there is an [issue](https://github.com/Azure/azure-cosmos-dotnet-v3/issues/4002) about that though). However, you can use the `AddClient` I've just mentioned.

```csharp
builder.Services.AddOptions<CosmosDbConfiguration>().BindConfiguration("Warehouse:CosmosDb");
builder.Services.AddAzureClients(clientBuilder =>
{
    clientBuilder.AddClient<CosmosClient, CosmosClientOptions>((_, _, serviceProvider) =>
    {
        var cosmosConfiguration = serviceProvider.GetRequiredService<IOptions<CosmosDbConfiguration>>().Value;
        return new CosmosClientBuilder(cosmosConfiguration.Endpoint, cosmosConfiguration.AuthKey)
            .WithSerializerOptions(new () { PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase })
            .Build();
    }).WithName("Warehouse");
}

```

You can note that using the `AddClient` method allows us to take profit from the named clients' feature.

## Wrapping up

As you have seen, the use of the [`Microsoft.Extensions.Azure`](https://www.nuget.org/packages/Microsoft.Extensions.Azure) package simplifies the registration and configuration of Azure clients. While providing you with a consistent way of handling the dependency injection for Azure SDKs, it also allows you to easily customize the authentication and other options available.

I hope you learned something. Don't hesitate to share your tips or what you like about the Azure SDKs in the comments.