【问题标题】:Dynamically Reading Azure App Configuration in .Net core Web App using .Net 6使用 .Net 6 在 .Net 核心 Web 应用程序中动态读取 Azure 应用程序配置
【发布时间】:2023-02-23 04:05:43
【问题描述】:

我想做什么:我正在尝试使用 .Net 6 Web API 应用程序在 Azure App Configuration 中设置 Azure App Configuration,目标是能够在 azure 中更改密钥,并且在我的应用程序中没有任何密钥会更新,直到 sentinel价值已经改变。理论上,这应该允许我安全地热插拔配置。

我的问题是:当我执行此操作时,IOptionsSnapshot 无法从 Azure 应用程序配置中获取值。我得到的值是空的,即使是第一次。 当我们使用 IConfiguration 时,我第一次可以获得这些值。

我参考的文档:我参考了 docs.microsoft 中的文档

  1. https://learn.microsoft.com/en-us/azure/azure-app-configuration/quickstart-aspnet-core-app?tabs=core6x
  2. https://learn.microsoft.com/en-us/azure/azure-app-configuration/enable-dynamic-configuration-aspnet-core?tabs=core5x

    对于动态更新,.Net 6 没有可用的参考,所以我在 .Net 5 中验证并更新了 .Net 6 的代码

    我的环境:使用从 Visual Studio Enterprise 2022 运行的 dot net 6,以及 Microsoft.Azure.AppConfiguration.AspNetCore 的最新 nuget 包

    我的代码: 程序.cs文件

    using ReadingConfiguration.Model;
    
    var builder = WebApplication.CreateBuilder(args);
    
    
    #region Start reading AppSettings.Json file
    //Reading appsettings.json Configuration file using
    builder.Services.Configure<MySettingsConfiguration>(
        builder.Configuration.GetSection("MySettings"));
    builder.Services.AddConfig(builder.Configuration);
    #endregion
    // Add services to the container.
    
    #region Code start for connecting the Azure App Configuration
    // Using to connect the azure App configuration
    var connectionString = builder.Configuration.GetConnectionString("AppConfig");
    builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
    {
        var settings = config.Build();
        config.AddAzureAppConfiguration(option =>
        {
            option.Connect(connectionString).ConfigureRefresh(refresh =>
            {
                refresh.Register("AAConfiguration:Sentinel", refreshAll:true).SetCacheExpiration(new TimeSpan(0, 0, 30));
            });
        });
    })
    .ConfigureServices(service =>
    {    service.AddControllers();   
    });
    //Middleware for refreshing the azure App configuration
    builder.Services.Configure<AAConfiguration>(builder.Configuration.GetSection("AAConfiguration"));
    builder.Services.AddAzureAppConfiguration();
    builder.Services.AddControllers();
    #endregion
    
    // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();
    
    var app = builder.Build();
    
    // Configure the HTTP request pipeline.
    // If statement can be removed if we need the swagger only in development
    // if (app.Environment.IsDevelopment())
    // {
        app.UseSwagger();
        app.UseSwaggerUI();
    // }
    //Middleware for refreshing the azure App configuration
    app.UseAzureAppConfiguration();
    app.UseHttpsRedirection();
    
    app.UseAuthorization();
    
    app.MapControllers();
    
    app.Run();
    

    读取 AzureAppConfigurationController 文件

     using Microsoft.AspNetCore.Http;
     using Microsoft.AspNetCore.Mvc;
     using Microsoft.Extensions.Configuration;
     using Microsoft.Extensions.Options;
     using ReadingConfiguration.Model;
        
     namespace ReadingConfiguration
     {
         [Route("api/[controller]")]
         [ApiController]
         public class ReadingAzureAppConfigurationController : ControllerBase
         {
             private readonly AAConfiguration _aaConfiguration;
             private readonly IConfiguration _configuration; 
        
             public ReadingAzureAppConfigurationController(IOptionsSnapshot<AAConfiguration> optionsSnapshot,IConfiguration configuration)
             {
                 _aaConfiguration = optionsSnapshot.Value;
                 _configuration = configuration;
             }
        
             [HttpGet]
             public string ReadingDynamicAzureAppConfiguration()
             {
                 return _aaConfiguration.Message;
             }
        
             [HttpGet]
             [Route("ReadingAppConfig")]
             public string ReadingAzureAppConfiguration()
             {
                 return _configuration["Message"];
             }
         }
     }
    

    AA配置文件

     namespace ReadingConfiguration.Model
     {
         public class AAConfiguration
         {
             public string? Message { get; set; }
             public int Sentinel { get; set; }
         }
     }
    

    Appsettings.Json

    {
       "Logging": {
         "LogLevel": {
           "Default": "Information",
           "Microsoft.AspNetCore": "Warning"
         }
       },
       "MySettings": {
         "Log": true,
         "ConnectionStringId": "Default",
         "Parameters": {
           "IsProduction": false
         }
       },
       "Trading": {
         "ChartType": "Monthly",
         "Function": "Pivot",
         "RSI": true
       },
       "Car": {
         "Manufacturer": "Fiat",
         "Model": "Punto",
         "Year": 2013
       },
       "AllowedHosts": "*"
     }
    

    MyConfigServiceCollectionExtensions 文件

    using Microsoft.Extensions.Configuration;
    using ReadingConfiguration.Model;
    
    namespace Microsoft.Extensions.DependencyInjection
    {
        public static class MyConfigServiceCollectionExtensions
        {
            public static IServiceCollection AddConfig(this IServiceCollection services, IConfiguration configuration)
            {
                #region This will read the configuration from appsettings.json
                services.Configure<TradingConfiguration>(
                    configuration.GetSection("Trading")
                    );
                services.Configure<CarConfiguration>(
                    configuration.GetSection("Car")
                    );
                #endregion
                // This will read the configuration azure app configuration
                services.Configure<AAConfiguration>(
                    configuration.GetSection("AAConfiguration")
                    );
                return services;
            }
        }
    }
    

    使用用户机密从本地计算机连接到应用程序配置。

    1. 创建秘密管理器密钥以在开发时连接到本地计算机中的 Azure 应用程序配置 dotnet 用户机密初始化

    2. 在 Secret Manager 中配置 Azure App 配置的连接字符串。

      dotnet user-secrets set ConnectionStrings:AppConfig“使用您的应用配置主连接字符串”

      1. 从 Nuget Microsoft.Azure.AppConfiguration.AspNetCore 添加包

【问题讨论】:

    标签: c# asp.net-core .net-core .net-6.0 azure-app-configuration


    【解决方案1】:

    请参考我的代码示例,它在我身边有效。我创建了一个新的 .net 6 api 项目。运行应用程序后,您可以更改 Azure 门户中的值以测试刷新。

    将连接字符串添加到 appsetting.json:

    "ConnectionStrings": {
        "AppConfig": "Endpoint=https://xxxx.azconfig.io;Id=yaFxxSgH;Secret=5MYxxxs="
      }
    

    我的program.cs,请不要忘记添加服务和中间件,中间件是用来监控sentinel key的。

    using WebApiNet6AzureAppConfig.Models;
    
    var builder = WebApplication.CreateBuilder(args);
    
    ConfigurationManager configuration = builder.Configuration;
    var connectionString = builder.Configuration.GetConnectionString("AppConfig");
    builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
    {
        var settings = config.Build();
        config.AddAzureAppConfiguration(options =>
        {
            options.Connect(connectionString)
                   .ConfigureRefresh(refresh =>
                   {
                       refresh.Register("TestApp:Settings:FontColor", refreshAll: true)
                                          .SetCacheExpiration(new TimeSpan(0, 0, 30));
                   });
        });
    }).ConfigureServices(services =>
        {
            services.AddControllers();
        });
    builder.Services.Configure<AppSettings>(configuration.GetSection("TestApp:Settings"));
    builder.Services.AddAzureAppConfiguration();
    
    builder.Services.AddControllers();
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();
    var app = builder.Build();
    // Configure the HTTP request pipeline.
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }
    
    app.UseAzureAppConfiguration();
    
    app.UseHttpsRedirection();
    app.UseAuthorization();
    app.MapControllers();
    app.Run();
    

    我的测试 api:

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Options;
    using WebApiNet6AzureAppConfig.Models;
    
    namespace WebApiNet6AzureAppConfig.Controllers
    {
        [ApiController]
        [Route("[controller]")]
        public class WeatherForecastController : ControllerBase
        {
            private readonly AppSettings _settings;
    
            public WeatherForecastController(IOptionsSnapshot<AppSettings> settings)
            {
                _settings = settings.Value;
            }
    
            [HttpGet(Name = "GetWeatherForecast")]
            public string Get()
            {
                var res = _settings.Message;
                return res;
            }
    
        }
    }
    

    【讨论】:

    • 谢谢 Tiny .. 根据你的输入,我做了两处更改,我第一次可以从 Azure App 配置中获取值,但它无法获取修改后的值。更改我所做的: 1. 在 azure 应用程序配置中,我将密钥创建为 AAConfiguration:Message 和 AAConfiguration.Sentinel。
    • 2.然后在扩展类中,我从应用程序配置中读取配置[我也在问题部分代码中包含了扩展方法]。但是正如我所说,如果 azure 应用程序配置中的 Sentinel 值发生任何更改,这些更改将无法刷新配置。然后我在 Program.cs 文件中的“连接 Azure 应用程序配置的代码开始”区域中进行了更改,如您提供的,但这也无法从 Azure 应用程序配置中获取刷新值。
    • 起初,我可以从 azure 应用程序配置中获取值,但如果我编辑 Sentinel 键的值,则无法获取刷新后的值。然后我发现它是 Program.cs 中缺少中间件的结果,然后我添加了 builder.Services.AddAzureAppConfiguration(); 和 app.UseAzureAppConfiguration(); 。请注意我所有的代码都是从您在问题中提供的文档中引用的,因此在 azure 中添加的键也是相同的。
    • 感谢@Tiny 在包含它开始工作的中间件之后。更新了 Program.cs 文件。
    • @Sreenivasan 如果你有工作代码,你能给我一个链接或代码 sn-p 我可以参考......我正在努力解决同样的问题。
    【解决方案2】:

    你能在这里帮忙吗 - 我被卡住了 - 按照所有步骤仍然没有从 Azure 应用程序配置中获取值。任何调用 [configuration.GetSection("Rental") 或 configuration.GetSection("Rental:Settings")] 返回空值! @小王

    using web.BackgorundServices;
    using web.Concretes;
    using web.Contracts;
    using web.Models;
    
    var builder = WebApplication.CreateBuilder(args);
    var configuration = builder.Configuration;
    
    builder.Host.ConfigureAppConfiguration((context, config) =>
    {
        if (context.HostingEnvironment.IsDevelopment())
        {
            config.AddAzureAppConfiguration(c =>
            {
                c.Connect(ConnectionStringVariable) // Copied from Access key section
                .ConfigureRefresh(refresh =>
                {
                    refresh.Register("Rental", refreshAll: true)
                                       .SetCacheExpiration(TimeSpan.FromSeconds(5));
                });
            });
        }    
    });
    
    
    builder.Services.Configure<AppSetings>(configuration.GetSection("Rental"));
    
    builder.Services.AddAzureAppConfiguration();
    
    // Add services to the container.
    builder.Services.AddControllersWithViews();
    
    var app = builder.Build();
    
    app.UseAzureAppConfiguration();
    
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();
    app.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
    
    app.Run();
    
    
    namespace web.Models
    {
        public class AppSetings
        {
            public string BackgroundColor { get; set; }      
            public string FontColor { get; set; }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2019-05-19
      • 2018-04-16
      • 2017-05-12
      • 2022-06-21
      • 1970-01-01
      • 2018-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多