Skip to content

Commit 021c081

Browse files
authored
Merge pull request #266694 from zhiyuanliang-ms/zhiyuanliang/dynamic-configuration-background-service
Azure App Configuration - Add dynamic configuration tutorial for .NET background service
2 parents a519b42 + 9422ea7 commit 021c081

6 files changed

+222
-2
lines changed

articles/azure-app-configuration/TOC.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@
6969
href: enable-dynamic-configuration-dotnet-core.md
7070
- name: .NET Framework
7171
href: enable-dynamic-configuration-dotnet.md
72+
- name: .NET Background Service
73+
href: enable-dynamic-configuration-dotnet-background-service.md
7274
- name: Azure Functions
7375
href: enable-dynamic-configuration-azure-functions-csharp.md
7476
- name: Spring Boot
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
---
2+
title: "Tutorial: Use dynamic configuration in a .NET background service"
3+
titleSuffix: Azure App Configuration
4+
description: In this tutorial, you learn how to dynamically update the configuration data for .NET background services.
5+
services: azure-app-configuration
6+
author: zhiyuanliang
7+
manager: zhenlan
8+
ms.service: azure-app-configuration
9+
ms.devlang: csharp
10+
ms.custom: devx-track-csharp, devx-track-dotnet
11+
ms.topic: tutorial
12+
ms.date: 02/20/2024
13+
ms.author: zhiyuanliang
14+
#Customer intent: I want to dynamically update my .NET background service to use the latest configuration data in App Configuration.
15+
---
16+
# Tutorial: Use dynamic configuration in a .NET background service
17+
18+
Data from App Configuration can be loaded as App Settings in a .NET application. For more information, see the [quickstart](./quickstart-dotnet-core-app.md). However, as is designed by the .NET, the App Settings can only refresh upon application restart. The App Configuration .NET provider is a .NET Standard library. It supports caching and refreshing configuration dynamically without application restart. This tutorial shows how you can implement dynamic configuration updates in a .NET background service.
19+
20+
In this tutorial, you learn how to:
21+
22+
> [!div class="checklist"]
23+
> * Set up your .NET background service to update its configuration in response to changes in an App Configuration store.
24+
> * Consume the latest configuration in your background service.
25+
26+
## Prerequisites
27+
28+
- An Azure account with an active subscription. [Create one for free](https://azure.microsoft.com/free/).
29+
- An App Configuration store. [Create a store](./quickstart-azure-app-configuration-create.md#create-an-app-configuration-store).
30+
- [.NET SDK 6.0 or later](https://dotnet.microsoft.com/download) - also available in the [Azure Cloud Shell](https://shell.azure.com).
31+
32+
## Add a key-value
33+
34+
Add the following key-value to the App Configuration store and leave **Label** and **Content Type** with their default values. For more information about how to add key-values to a store using the Azure portal or the CLI, go to [Create a key-value](./quickstart-azure-app-configuration-create.md#create-a-key-value).
35+
36+
| Key | Value |
37+
|----------------------------|-------------------------------------|
38+
| *TestApp:Settings:Message* | *Data from Azure App Configuration* |
39+
40+
## Create a .NET background service
41+
42+
You use the [.NET command-line interface (CLI)](/dotnet/core/tools/) to create a new .NET app project. The advantage of using the .NET CLI over Visual Studio is that it's available across the Windows, macOS, and Linux platforms. Alternatively, use the preinstalled tools available in the [Azure Cloud Shell](https://shell.azure.com).
43+
44+
1. Create a new folder for your project.
45+
46+
2. In the new folder, run the following command to create a new .NET background service project:
47+
48+
```dotnetcli
49+
dotnet new worker
50+
```
51+
52+
## Reload data from App Configuration
53+
54+
1. Add references to the `Microsoft.Extensions.Configuration.AzureAppConfiguration` NuGet package by running the following commands:
55+
56+
```dotnetcli
57+
dotnet add package Microsoft.Extensions.Configuration.AzureAppConfiguration
58+
```
59+
60+
1. Run the following command to restore packages for your project:
61+
62+
```dotnetcli
63+
dotnet restore
64+
```
65+
66+
1. Open *Program.cs* and add the following statements:
67+
68+
```csharp
69+
using Microsoft.Extensions.Configuration;
70+
using Microsoft.Extensions.Configuration.AzureAppConfiguration;
71+
```
72+
73+
1. Connect to App Configuration.
74+
75+
```csharp
76+
// Existing code in Program.cs
77+
// ... ...
78+
79+
var builder = Host.CreateApplicationBuilder(args);
80+
81+
builder.Configuration.AddAzureAppConfiguration(options =>
82+
{
83+
options.Connect(Environment.GetEnvironmentVariable("ConnectionString"))
84+
// Load all keys that start with `TestApp:`.
85+
.Select("TestApp:*")
86+
// Configure to reload the key 'TestApp:Settings:Message' if it is modified.
87+
.ConfigureRefresh(refreshOptions =>
88+
{
89+
refreshOptions.Register("TestApp:Settings:Message");
90+
});
91+
92+
// Register the refresher so that the Worker service can consume it through DI
93+
builder.Services.AddSingleton(options.GetRefresher());
94+
});
95+
96+
// The rest of existing code in Program.cs
97+
// ... ...
98+
```
99+
100+
In the `ConfigureRefresh` method, a key within your App Configuration store is registered for change monitoring. The `Register` method has an optional boolean parameter `refreshAll` that can be used to indicate whether all configuration values should be refreshed if the registered key changes. In this example, only the key *TestApp:Settings:Message* will be refreshed. All settings registered for refresh have a default cache expiration of 30 seconds before a new refresh is attempted. It can be updated by calling the `AzureAppConfigurationRefreshOptions.SetCacheExpiration` method.
101+
102+
1. Open *Worker.cs*. Inject `IConfiguration` and `IConfigurationRefresher` to the `Worker` service and log the configuration data from App Configuration.
103+
104+
```csharp
105+
public class Worker : BackgroundService
106+
{
107+
private readonly ILogger<Worker> _logger;
108+
private readonly IConfiguration _configuration;
109+
private readonly IConfigurationRefresher _refresher;
110+
111+
public Worker(ILogger<Worker> logger, IConfiguration configuration, IConfigurationRefresher refresher)
112+
{
113+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
114+
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
115+
_refresher = refresher ?? throw new ArgumentNullException(nameof(refresher));
116+
}
117+
118+
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
119+
{
120+
while (!stoppingToken.IsCancellationRequested)
121+
{
122+
// Intentionally not await TryRefreshAsync to avoid blocking the execution.
123+
_refresher.TryRefreshAsync(stoppingToken);
124+
125+
if (_logger.IsEnabled(LogLevel.Information))
126+
{
127+
_logger.LogInformation(_configuration["TestApp:Settings:Message"] ?? "No data.");
128+
}
129+
130+
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
131+
}
132+
}
133+
}
134+
```
135+
136+
Calling the `ConfigureRefresh` method alone won't cause the configuration to refresh automatically. You call the `TryRefreshAsync` method from the interface `IConfigurationRefresher` to trigger a refresh. This design is to avoid requests sent to App Configuration even when your application is idle. You can include the `TryRefreshAsync` call where you consider your application active. For example, it can be when you process an incoming message, an order, or an iteration of a complex task. It can also be in a timer if your application is active all the time. In this example, you call `TryRefreshAsync` every time the background service is executed. Note that, even if the call `TryRefreshAsync` fails for any reason, your application will continue to use the cached configuration. Another attempt will be made when the configured cache expiration time has passed and the `TryRefreshAsync` call is triggered by your application activity again. Calling `TryRefreshAsync` is a no-op before the configured cache expiration time elapses, so its performance impact is minimal, even if it's called frequently.
137+
138+
## Build and run the app locally
139+
140+
1. Set an environment variable named **ConnectionString**, and set it to the access key to your App Configuration store. At the command line, run the following command.
141+
142+
### [Windows command prompt](#tab/windowscommandprompt)
143+
144+
To build and run the app locally using the Windows command prompt, run the following command.
145+
146+
```console
147+
setx ConnectionString "connection-string-of-your-app-configuration-store"
148+
```
149+
150+
Restart the command prompt to allow the change to take effect. Print the value of the environment variable to validate that it's set properly.
151+
152+
### [PowerShell](#tab/powershell)
153+
154+
If you use Windows PowerShell, run the following command.
155+
156+
```azurepowershell
157+
$Env:ConnectionString = "connection-string-of-your-app-configuration-store"
158+
```
159+
160+
### [macOS](#tab/unix)
161+
162+
If you use macOS, run the following command.
163+
164+
```console
165+
export ConnectionString='connection-string-of-your-app-configuration-store'
166+
```
167+
168+
### [Linux](#tab/linux)
169+
170+
If you use Linux, run the following command.
171+
172+
```console
173+
export ConnectionString='connection-string-of-your-app-configuration-store'
174+
```
175+
176+
---
177+
178+
1. Run the following command to build the console app.
179+
180+
```dotnetcli
181+
dotnet build
182+
```
183+
184+
1. After the build successfully completes, run the following command to run the app locally.
185+
186+
```dotnetcli
187+
dotnet run
188+
```
189+
190+
1. You should see the following outputs in the console.
191+
192+
![Screenshot of the background service.](./media/dotnet-background-service-run.png)
193+
194+
1. In the Azure portal, navigate to the **Configuration explorer** of your App Configuration store, and update the value of the following key.
195+
196+
| Key | Value |
197+
|----------------------------|-----------------------------------------------|
198+
| *TestApp:Settings:Message* | *Data from Azure App Configuration - Updated* |
199+
200+
1. Wait for about 30 seconds. You should see the console outputs changed.
201+
202+
![Screenshot of the refreshed background service.](./media/dotnet-background-service-refresh.png)
203+
204+
## Clean up resources
205+
206+
[!INCLUDE [azure-app-configuration-cleanup](../../includes/azure-app-configuration-cleanup.md)]
207+
208+
## Next steps
209+
210+
In this tutorial, you enabled your .NET background service to dynamically refresh configuration settings from App Configuration. To learn how to enable dynamic configuration in an ASP.NET Web Application, continue to the next tutorial:
211+
212+
> [!div class="nextstepaction"]
213+
> [Enable dynamic configuration in ASP.NET Web Applications](./enable-dynamic-configuration-aspnet-core.md)
214+
215+
To learn how to use an Azure managed identity to streamline the access to App Configuration, continue to the next tutorial:
216+
217+
> [!div class="nextstepaction"]
218+
> [Managed identity integration](./howto-integrate-azure-managed-service-identity.md)

articles/azure-app-configuration/enable-dynamic-configuration-dotnet-core.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ if (_refresher != null)
7272

7373
In the `ConfigureRefresh` method, a key within your App Configuration store is registered for change monitoring. The `Register` method has an optional boolean parameter `refreshAll` that can be used to indicate whether all configuration values should be refreshed if the registered key changes. In this example, only the key *TestApp:Settings:Message* will be refreshed. The `SetCacheExpiration` method specifies the minimum time that must elapse before a new request is made to App Configuration to check for any configuration changes. In this example, you override the default expiration time of 30 seconds, specifying a time of 10 seconds instead for demonstration purposes.
7474

75-
Calling the `ConfigureRefresh` method alone won't cause the configuration to refresh automatically. You call the `TryRefreshAsync` method from the interface `IConfigurationRefresher` to trigger a refresh. This design is to avoid phantom requests sent to App Configuration even when your application is idle. You'll want to include the `TryRefreshAsync` call where you consider your application active. For example, it can be when you process an incoming message, an order, or an iteration of a complex task. It can also be in a timer if your application is active all the time. In this example, you call `TryRefreshAsync` every time you press the Enter key. Even if the call `TryRefreshAsync` fails for any reason, your application continues to use the cached configuration. Another attempt is made when the configured cache expiration time has passed and the `TryRefreshAsync` call is triggered by your application activity again. Calling `TryRefreshAsync` is a no-op before the configured cache expiration time elapses, so its performance impact is minimal, even if it's called frequently.
75+
Calling the `ConfigureRefresh` method alone won't cause the configuration to refresh automatically. You call the `TryRefreshAsync` method from the interface `IConfigurationRefresher` to trigger a refresh. This design is to avoid requests sent to App Configuration even when your application is idle. You'll want to include the `TryRefreshAsync` call where you consider your application active. For example, it can be when you process an incoming message, an order, or an iteration of a complex task. It can also be in a timer if your application is active all the time. In this example, you call `TryRefreshAsync` every time you press the Enter key. Even if the call `TryRefreshAsync` fails for any reason, your application continues to use the cached configuration. Another attempt is made when the configured cache expiration time has passed and the `TryRefreshAsync` call is triggered by your application activity again. Calling `TryRefreshAsync` is a no-op before the configured cache expiration time elapses, so its performance impact is minimal, even if it's called frequently.
7676

7777
### Configuration refresh using dependency injection
7878

articles/azure-app-configuration/enable-dynamic-configuration-dotnet.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ Add the following key-value to the App Configuration store and leave **Label** a
106106
}
107107
```
108108

109-
Calling the `ConfigureRefresh` method alone won't cause the configuration to refresh automatically. You call the `TryRefreshAsync` method from the interface `IConfigurationRefresher` to trigger a refresh. This design is to avoid phantom requests sent to App Configuration even when your application is idle. You can include the `TryRefreshAsync` call where you consider your application active. For example, it can be when you process an incoming message, an order, or an iteration of a complex task. It can also be in a timer if your application is active all the time. In this example, you call `TryRefreshAsync` when you press the Enter key. Note that, even if the call `TryRefreshAsync` fails for any reason, your application will continue to use the cached configuration. Another attempt will be made when the configured cache expiration time has passed and the `TryRefreshAsync` call is triggered by your application activity again. Calling `TryRefreshAsync` is a no-op before the configured cache expiration time elapses, so its performance impact is minimal, even if it's called frequently.
109+
Calling the `ConfigureRefresh` method alone won't cause the configuration to refresh automatically. You call the `TryRefreshAsync` method from the interface `IConfigurationRefresher` to trigger a refresh. This design is to avoid requests sent to App Configuration even when your application is idle. You can include the `TryRefreshAsync` call where you consider your application active. For example, it can be when you process an incoming message, an order, or an iteration of a complex task. It can also be in a timer if your application is active all the time. In this example, you call `TryRefreshAsync` when you press the Enter key. Note that, even if the call `TryRefreshAsync` fails for any reason, your application will continue to use the cached configuration. Another attempt will be made when the configured cache expiration time has passed and the `TryRefreshAsync` call is triggered by your application activity again. Calling `TryRefreshAsync` is a no-op before the configured cache expiration time elapses, so its performance impact is minimal, even if it's called frequently.
110110

111111
## Build and run the app locally
112112

101 KB
Loading
55.7 KB
Loading

0 commit comments

Comments
 (0)