อัปเดตการกำหนดค่า. net core แบบไดนามิกจาก Azure App Configuration


9

สิ่งที่ฉันพยายามจะทำ: ฉันกำลังพยายามติดตั้ง Azure App Configuration ด้วย. net core 2.1 mvc เว็บแอปพลิเคชันพร้อมคีย์ Sentinel ใน Azure App Configuration โดยมีเป้าหมายในการเปลี่ยนคีย์เป็น Azure และไม่มีกุญแจ จะอัปเดตในแอพของฉันจนกว่าค่า Sentinel จะเปลี่ยนไป ตามทฤษฎีแล้วสิ่งนี้จะช่วยให้ฉันสามารถกำหนดค่าการแลกเปลี่ยนความร้อนได้อย่างปลอดภัย

ปัญหาของฉันคืออะไร: เมื่อฉันทำสิ่งนี้ไม่มีเมธอด WatchAndReloadAll () ให้ดูยามใน IWebHostBuilder และเมธอด Refresh () อื่นดูเหมือนจะไม่รีเฟรชการกำหนดค่าตามที่ระบุ

ข้อมูลความเป็นมาและสิ่งที่ฉันได้ลอง: ฉันเข้าร่วม VS Live - San Diego เมื่อสัปดาห์ที่แล้วและดูการสาธิตเกี่ยวกับการกำหนดค่าแอป Azure ฉันมีปัญหาบางอย่างในการพยายามให้แอปพลิเคชันรีเฟรชค่าการกำหนดค่าเมื่อใช้งานดังนั้นฉันจึงอ้างอิงการสาธิตนี้ซึ่งอธิบายถึงวิธีการทำเช่นนี้ด้วย ส่วนที่เกี่ยวข้องอยู่ที่ประมาณ 10 นาทีอย่างไรก็ตามวิธีดังกล่าวดูเหมือนจะไม่สามารถใช้ได้ใน IWebHostBuilder

เอกสารที่ฉันกำลังอ้างอิง: ในเอกสารอย่างเป็นทางการไม่มีการอ้างอิงถึงวิธีการนี้ดูdoc. เริ่มต้นอย่างรวดเร็ว. coreและการกำหนดค่าแบบไดนามิก doc doc แกน.

สภาพแวดล้อมของฉัน: การใช้ดอทเน็ตคอร์ 2.1 ถูกเรียกใช้จาก Visual Studio Enterprise 2019 พร้อมด้วยแพคเกจ nuget แสดงตัวอย่างล่าสุดสำหรับ Microsoft.Azure.AppConfiguration.AspNetCore 2.0.0-preview-010060003-1250

รหัสของฉัน: ในการสาธิตพวกเขาสร้าง IWebHostBuilder ผ่านวิธีการ CreateWebHostBuilder (string [] args) ดังนี้:

public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
    return WebHost.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((hostingContext, config) =>
    {
        var settings = config.Build();
        config.AddAzureAppConfiguration(options =>
        {
            options.Connect(settings["ConnectionStrings:AzureConfiguration"])
            .Use(keyFilter: "TestApp:*")
            .WatchAndReloadAll(key: "TestApp:Sentinel", pollInterval: TimeSpan.FromSeconds(5));
        }); 
    })
    .UseStartup<Startup>();
}

ฉันก็ลองแบบนี้โดยใช้เอกสารปัจจุบัน:

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((hostingContext, config) =>
    {
        var settings = config.Build();

        config.AddAzureAppConfiguration(options =>
        {
            // fetch connection string from local config. Could use KeyVault, or Secrets as well.
            options.Connect(settings["ConnectionStrings:AzureConfiguration"])
            // filter configs so we are only searching against configs that meet this pattern
            .Use(keyFilter: "WebApp:*")
            .ConfigureRefresh(refreshOptions =>
            { 
                // In theory, when this value changes, on the next refresh operation, the config will update all modified configs since it was last refreshed.
                refreshOptions.Register("WebApp:Sentinel", true);
                refreshOptions.Register("WebApp:Settings:BackgroundColor", false);
                refreshOptions.Register("WebApp:Settings:FontColor", false);
                refreshOptions.Register("WebApp:Settings:FontSize", false);
                refreshOptions.Register("WebApp:Settings:Message", false);
            });
        });
    })
    .UseStartup<Startup>();

จากนั้นในระดับเริ่มต้นของฉัน:

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<Settings>(Configuration.GetSection("WebApp:Settings"));
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseAzureAppConfiguration();
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

และในที่สุดรูปแบบการตั้งค่าของฉัน:

public class Settings
{
    public string BackgroundColor { get; set; }
    public long FontSize { get; set; }
    public string FontColor { get; set; }
    public string Message { get; set; }
}

ตอนนี้ในตัวควบคุมของฉันฉันดึงการตั้งค่าเหล่านั้นแล้วโยนลงในถุงมุมมองเพื่อให้แสดงในมุมมอง

public class HomeController : Controller
{
    private readonly Settings _Settings;

    public HomeController(IOptionsSnapshot<Settings> settings)
    {
        _Settings = settings.Value;
    }

    public IActionResult Index()
    {
        ViewData["BackgroundColor"] = _Settings.BackgroundColor;
        ViewData["FontSize"] = _Settings.FontSize;
        ViewData["FontColor"] = _Settings.FontColor;
        ViewData["Message"] = _Settings.Message;

        return View();
    }
}

มุมมองที่เรียบง่ายเพื่อแสดงการเปลี่ยนแปลง:

<!DOCTYPE html>
<html lang="en">
<style>
    body {
        background-color: @ViewData["BackgroundColor"]
    }
    h1 {
        color: @ViewData["FontColor"];
        font-size: @ViewData["FontSize"];
    }
</style>
<head>
    <title>Index View</title>
</head>
<body>
    <h1>@ViewData["Message"]</h1>
</body>
</html>

ฉันสามารถทำให้มันดึงการกำหนดค่าลงได้ในครั้งแรกอย่างไรก็ตามฟังก์ชั่นการรีเฟรชจะไม่ทำงาน แต่อย่างใด

ในตัวอย่างล่าสุดฉันคาดว่าการกำหนดค่าให้อัปเดตเมื่อตั้งค่า Sentinel เป็นค่าใหม่ใด ๆ หรืออย่างน้อยที่สุดเพื่ออัปเดตค่า 30 วินาทีหลังจากเปลี่ยนไป ไม่มีความยาวของการรอคอยอัปเดตค่าและมีเพียงการปิดและรีสตาร์ทแอพเท่านั้นที่โหลดการกำหนดค่าใหม่

อัปเดต: การเพิ่ม app.UseAzureAppConfiguration (); ในวิธีการกำหนดค่าเมื่อเริ่มต้นและการตั้งค่าการหมดเวลาที่ชัดเจนในแคชสำหรับการกำหนดค่าแก้ไขวิธีการรีเฟรชเพื่อรีเฟรชหลังจากระยะเวลาคงที่ แต่ฟังก์ชันการทำงานของ Sentinel ยังคงไม่ทำงานและไม่มีการตั้งค่าสถานะ


คุณช่วยแสดงให้ฉันเห็นว่าคุณเข้าถึงการกำหนดค่าได้อย่างไร ฉันเลียนแบบสถานการณ์ของคุณในหนึ่งในโครงการของตัวเองและทำงานได้อย่างสมบูรณ์แบบ
Peter Bons

ฉันคาดว่าการกำหนดค่าบางอย่างมีผลผูกพันบางแห่งในConfigureServicesวิธีการของคุณใน startuop.cs เช่น services.Configure<LogSettings>(configuration.GetSection("LogSettings"));
Peter Bons

@peterBons ลิงก์ของคุณพาฉันไปที่ 404
Nick Gasia Robitsch

@PeterBons ฉันได้อัปเดตโพสต์ของฉันเพื่อรวมข้อมูลที่ต้องการในการกำหนดค่าการฉีด / การรวม ฉันไม่คิดว่ามันจะเกี่ยวข้องในขณะนั้นเพราะมันใช้งานได้
Nick Gasia Robitsch

1
นั่นมัน ไม่เป็นไร
Peter Bons

คำตอบ:


6

โอเคหลังจากการทดสอบและลองผิดลองถูกมากฉันทำงานได้แล้ว

ปัญหาของฉันคือบริการหายไปสำหรับสีฟ้าบนวิธีการกำหนดค่า มีพฤติกรรมที่น่าสนใจบางอย่างที่นี่ซึ่งมันจะยังดึงลงการตั้งค่ามันก็จะไม่อัปเดตถ้ามันหายไป ดังนั้นเมื่อสิ่งนี้ถูกใส่เข้ามาและด้วย Sentinel ที่เหมาะสมซึ่งถูกกำหนดค่าต่อเอกสารมันทำงานได้กับการตั้งค่า updateAll อย่างไรก็ตามสิ่งนี้ยังไม่ได้จัดทำเป็นเอกสาร

นี่คือทางออก:

ใน Program.cs:

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration.AzureAppConfiguration;

namespace ASPNetCoreApp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }   // Main

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                var settings = config.Build();

                config.AddAzureAppConfiguration(options =>
                {
                    // fetch connection string from local config. Could use KeyVault, or Secrets as well.
                    options.Connect(settings["ConnectionStrings:AzureConfiguration"])
                    // filter configs so we are only searching against configs that meet this pattern
                    .Use(keyFilter: "WebApp:*")
                    .ConfigureRefresh(refreshOptions =>
                    { 
                        // When this value changes, on the next refresh operation, the config will update all modified configs since it was last refreshed.
                        refreshOptions.Register("WebApp:Sentinel", true);
                        // Set a timeout for the cache so that it will poll the azure config every X timespan.
                        refreshOptions.SetCacheExpiration(cacheExpirationTime: new System.TimeSpan(0, 0, 0, 15, 0));
                    });
                });
            })
            .UseStartup<Startup>();
    }
}

จากนั้นใน Startup.cs:

using ASPNetCoreApp.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace ASPNetCoreApp
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // bind the config to our DI container for the settings we are pulling down from azure.
            services.Configure<Settings>(Configuration.GetSection("WebApp:Settings"));
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }
            // Set the Azure middleware to handle configuration
            // It will pull the config down without this, but will not refresh.
            app.UseAzureAppConfiguration();
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

รูปแบบการตั้งค่าที่ฉันเชื่อมโยงกับข้อมูลที่เรียกคืนเป็นสีฟ้าของฉันไปที่:

namespace ASPNetCoreApp.Models
{
    public class Settings
    {
        public string BackgroundColor { get; set; }
        public long FontSize { get; set; }
        public string FontColor { get; set; }
        public string Message { get; set; }
    }
}

ตัวควบคุมโฮมทั่วไปที่มีการตั้งค่าคอนฟิกเป็น ViewBag เพื่อส่งต่อไปยังมุมมองของเรา:

using ASPNetCoreApp.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System.Diagnostics;

namespace ASPNetCoreApp.Controllers
{
    public class HomeController : Controller
    {
        private readonly Settings _Settings;

        public HomeController(IOptionsSnapshot<Settings> settings)
        {
            _Settings = settings.Value;
        }
        public IActionResult Index()
        {
            ViewData["BackgroundColor"] = _Settings.BackgroundColor;
            ViewData["FontSize"] = _Settings.FontSize;
            ViewData["FontColor"] = _Settings.FontColor;
            ViewData["Message"] = _Settings.Message;

            return View();
        }

        public IActionResult About()
        {
            ViewData["Message"] = "Your application description page.";

            return View();
        }

        public IActionResult Contact()
        {
            ViewData["Message"] = "Your contact page.";

            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}

มุมมองของเรา:

<!DOCTYPE html>
<html lang="en">
<style>
    body {
        background-color: @ViewData["BackgroundColor"]
    }
    h1 {
        color: @ViewData["FontColor"];
        font-size: @ViewData["FontSize"];
    }
</style>
<head>
    <title>Index View</title>
</head>
<body>
    <h1>@ViewData["Message"]</h1>
</body>
</html>

หวังว่านี่จะช่วยคนอื่นได้!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.