Skip to content

Commit

Permalink
Add Azure SDK DI doc (dotnet#24209)
Browse files Browse the repository at this point in the history
Thank you so much for this doc, @lilyjma!! It looks great. Great job! Merging.
  • Loading branch information
lilyjma committed May 18, 2021
1 parent 34488a7 commit 32dbd64
Show file tree
Hide file tree
Showing 2 changed files with 247 additions and 3 deletions.
8 changes: 5 additions & 3 deletions docs/azure/TOC.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
- name: Key Azure services for .NET developers
href: key-azure-services.md
- name: Configure your development environment
items:
items:
- name: Create an Azure account
href: create-azure-account.md
- name: Configure Visual Studio for Azure development (Windows)
Expand All @@ -20,7 +20,7 @@
- name: Install additional tools
href: azure-tools.md
- name: Development setup checklist
href: dotnet-dev-env-checklist.md
href: dotnet-dev-env-checklist.md
- name: Migrate to Azure
items:
- name: Choose the right Azure hosting option
Expand All @@ -36,7 +36,9 @@
- name: What is the Azure SDK for .NET?
href: ./sdk/azure-sdk-for-dotnet.md
- name: Authentication
href: authentication.md
href: authentication.md
- name: Dependency injection
href: dependency-injection.md
- name: Logging
href: logging.md
- name: Configuring a proxy server
Expand Down
242 changes: 242 additions & 0 deletions docs/azure/dependency-injection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
---
title: Dependency injection with the Azure .NET SDK
description: Learn how to use dependency injection with the Azure SDK for .NET client libraries
ms.date: 05/12/2021
ms.author: pakrym
author: pakrym
---

# Dependency injection with the Azure .NET SDK

This article demonstrates how to register Azure service clients from the [latest Azure .NET SDKs](https://azure.github.io/azure-sdk/releases/latest/index.html) in an ASP.NET Core application. Every ASP.NET Core application starts by booting up the application using the instructions provided in the `Startup` class. This includes a `ConfigureServices` method that is an ideal place to configure clients.

To configure the service clients, first add the following NuGet packages to your project:

- [Microsoft.Extensions.Azure](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/extensions/Microsoft.Extensions.Azure/README.md)
- [Azure.Identity](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/identity/Azure.Identity/README.md)
- The `Azure.*` package you'd like to use.

The sample code in this article will use Key Vault secrets and Blob Storage for demonstration purposes.

```dotnetcli
dotnet add package Microsoft.Extensions.Azure
dotnet add package Azure.Identity
dotnet add package Azure.Security.KeyVault.Secrets
dotnet add package Azure.Storage.Blobs
```

## Register client

In the `ConfigureServices` method, register a client for each service:

```csharp
public void ConfigureServices(IServiceCollection services)
{
services.AddAzureClients(builder =>
{
// Add a KeyVault client
builder.AddSecretClient(keyVaultUrl);

// Add a Storage account client
builder.AddBlobServiceClient(storageUrl);

// Use DefaultAzureCredential by default
builder.UseCredential(new DefaultAzureCredential());
});

services.AddControllers();
}
```

In code above, you need to explicitly specify the `keyVaultUrl` and `storageUrl`. Both variables are `Uri` types. The [Store configuration separately from code](#store-configuration-separately-from-code) section shows how you can avoid specifying the urls explicitly.

The code above uses `DefaultAzureCredential` for authentication. [DefaultAzureCredential](https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/identity/Azure.Identity#defaultazurecredential) will choose the best authentication mechanism based on your environment, allowing you to move your app seamlessly from development to production with no code changes.

## Use the registered clients

With the clients registered in `Startup`, you can now use them:

```csharp
[ApiController]
[Route("[controller]")]
public class MyApiController : ControllerBase
{
private readonly BlobServiceClient _blobServiceClient;

public MyApiController(BlobServiceClient blobServiceClient)
{
_blobServiceClient = blobServiceClient;
}

/// Get a list of all the blobs in the demo container
[HttpGet]
public async Task<IEnumerable<string>> Get()
{
var containerClient = _blobServiceClient.GetBlobContainerClient("demo");
var results = new List<string>();
await foreach (BlobItem blob in containerClient.GetBlobsAsync())
{
results.Add(blob.Name);
}
return results.ToArray();
}
}
```

## Store configuration separately from code

In the [Register client](#register-client) section, you explicitly specify the `keyVaultUrl` and `storageUrl`. This could cause problems when you run code against different environments during development and production. The .NET team suggests [storing such configurations in environment dependent JSON files](/dotnet/core/extensions/configuration-providers#json-configuration-provider). Thus, you can have an _appsettings.Development.json_ file with one set of settings and an _appsettings.Production.json_ with another set of configurations. The format of the file is:

```json
{
"AzureDefaults": {
"Diagnostics": {
"IsTelemetryDisabled": false,
"IsLoggingContentEnabled": true
},
"Retry": {
"MaxRetries": 3,
"Mode": "Exponential"
}
},
"KeyVault": {
"VaultUri": "https://mykeyvault.vault.azure.net"
},
"Storage": {
"ServiceUri": "https://mydemoaccount.storage.windows.net"
}
}
```

You can add any options from the <xref:Azure.Core.ClientOptions> into the `AzureDefaults` section. One of the options is the retry policy. For more information, see [Configure a new try policy](#configure-a-new-retry-policy).

Since the `Configuration` object is injected from the host and stored inside the `Startup` constructor, you can do the following:

```csharp
public void ConfigureServices(IServiceCollection services)
{
services.AddAzureClients(builder =>
{
// Add a KeyVault client
builder.AddSecretClient(Configuration.GetSection("KeyVault"));

// Add a storage account client
builder.AddBlobServiceClient(Configuration.GetSection("Storage"));

// Use DefaultAzureCredential by default
builder.UseCredential(new DefaultAzureCredential());

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

services.AddControllers();
}
```

## Configure multiple service clients with different names

Say you have two storage accounts – one for private information and one for public information. Your application transfers data from the public to private storage account after some operation. You need to have two storage service clients. To set this up in `Startup.ConfigureServices`:

```csharp
public void ConfigureServices(IServiceCollection services)
{
services.AddAzureClients(builder =>
{
builder.AddBlobServiceClient(Configuration.GetSection("PublicStorage"));
builder.AddBlobServiceClient(Configuration.GetSection("PrivateStorage"))
.WithName("PrivateStorage");
});
}
```

In your controllers, you can access the named service clients using the <xref:Microsoft.Extensions.Azure.IAzureClientFactory%601?displayProperty=nameWithType>:

```csharp
public class HomeControllers : Controller
{
private readonly BlobServiceClient _publicStorage;
private readonly BlobServiceClient _privateStorage;

public HomeController(BlobServiceClient defaultClient, IAzureClientFactory<BlobServiceClient> clientFactory)
{
_publicStorage = defaultClient;
_privateStorage = clientFactory.GetClient("PrivateStorage");
}
}
```

The un-named service client is still available in the same way as before. Named clients are additive to this.

## Configure a new retry policy

At some point, you might want to change the default settings for a service client. You may want different retry settings or to use a different service API version, for example. You can set the retry settings globally or on a per service basis. Say you have the following _appsettings.json_ file:

```json
{
"AzureDefaults": {
"Retry": {
"maxTries": 3
}
},
"KeyVault": {
"VaultUri": "https://mykeyvault.vault.azure.net"
},
"Storage": {
"ServiceUri": "https://store1.storage.windows.net"
},
"CustomStorage": {
"ServiceUri": "https://store2.storage.windows.net"
}
}
```

You could change the retry policy depending on your needs like so:

```csharp
public void ConfigureServices(IServiceCollection services)
{
services.AddAzureClients(builder =>
{
// Establish the global defaults
builder.ConfigureDefaults(Configuration.GetSection("AzureDefaults"));
builder.UseCredential(new DefaultAzureCredential());

// A Key Vault Secrets client using the global defaults
builder.AddSecretClient(Configuration.GetSection("KeyVault"));

// A Storage client with a custom retry policy
builder.AddBlobServiceClient(Configuration.GetSection("Storage"))
.ConfigureOptions(options => options.Retry.MaxRetries = 10);

// A named storage client with a different custom retry policy
builder.AddBlobServiceClient(Configuration.GetSection("CustomStorage"))
.WithName("CustomStorage")
.ConfigureOptions(options =>
{
options.Retry.Mode = Azure.Core.RetryMode.Exponential;
options.Retry.MaxRetries = 5;
options.Retry.MaxDelay = TimeSpan.FromSections(120);
});
});
}
```

You can also place policy overrides in the _appsettings.json_ file:

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

## See also

- [Dependency injection in .NET](/dotnet/core/extensions/dependency-injection)
- [Configuration in .NET](/dotnet/core/extensions/configuration)
- [Configuration in ASP.NET Core](/aspnet/core/fundamentals/configuration)

0 comments on commit 32dbd64

Please sign in to comment.