Tự động cập nhật cấu hình lõi .net từ Cấu hình ứng dụng Azure


9

Những gì tôi đang cố gắng thực hiện: Tôi đang cố gắng thiết lập Cấu hình ứng dụng Azure với ứng dụng web 2.1 mvc lõi .net với khóa gửi trong Cấu hình ứng dụng Azure, với mục tiêu có thể thay đổi các phím theo phương vị và không có phím nào sẽ cập nhật trong ứng dụng của tôi cho đến khi giá trị sentinel thay đổi. Về lý thuyết, điều này sẽ cho phép tôi cấu hình trao đổi nóng một cách an toàn.

Vấn đề của tôi là: Khi tôi làm điều này, không có phương thức WatchAndReloadAll () có sẵn để xem sentinel trên IWebhostBuilder và các phương thức Refresh () thay thế dường như không làm mới cấu hình khi chúng ở trạng thái.

Thông tin cơ bản và những gì tôi đã thử: Tôi đã tham dự VS Live - San Diego, tuần trước và xem bản demo về Cấu hình ứng dụng Azure. Tôi đã gặp một số vấn đề khi cố gắng để ứng dụng làm mới các giá trị cấu hình khi cài đặt nó, vì vậy tôi cũng đã tham khảo bản demo này mô tả cách thực hiện điều này. Phần có liên quan nằm trong khoảng 10 phút. Tuy nhiên, phương pháp đó dường như không có sẵn trên IWebhostBuilder.

Tài liệu tôi đang tham khảo: Trong tài liệu chính thức không có tài liệu tham khảo về phương pháp này, hãy xem doc quickstart .net coredoc Dynamic configure .net core

Môi trường của tôi: Sử dụng dot net core 2.1 được chạy từ Visual Studio Enterprise 2019, với gói nuget xem trước mới nhất cho Microsoft.Azure.AppConfiguration.AspNetCore 2.0.0-preview-010060003-1250

Mã của tôi: Trong bản demo, họ đã tạo IWebhostBuilder thông qua phương thức CreatWebhostBuilder (chuỗi [] args) như vậy:

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>();
}

Tôi cũng đã thử nó theo cách này, sử dụng tài liệu hiện tại:

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>();

Sau đó, trong lớp khởi nghiệp của tôi:

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?}");
    });
}

và cuối cùng là mô hình cấu hình cài đặt của tôi:

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

Bây giờ, trong bộ điều khiển của mình, tôi kéo các cài đặt đó và ném chúng vào một túi xem để hiển thị trên màn hình.

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();
    }
}

Một cái nhìn đơn giản để hiển thị các thay đổi:

<!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>

Tôi có thể lấy nó để kéo cấu hình xuống lần đầu tiên, tuy nhiên, chức năng làm mới dường như không hoạt động theo bất kỳ cách nào.

Trong ví dụ trước, tôi dự kiến ​​các cấu hình sẽ cập nhật khi sentinel được đặt thành bất kỳ giá trị mới nào, hoặc ít nhất, để cập nhật giá trị 30 giây sau khi thay đổi. Không có thời gian chờ đợi cập nhật các giá trị và chỉ tắt và khởi động lại hoàn toàn ứng dụng mới tải cấu hình mới.

Cập nhật: Thêm ứng dụng.UseAzureAppConfiguration (); trong phương thức cấu hình khi khởi động và thiết lập thời gian chờ rõ ràng trên bộ đệm cho cấu hình đã cố định phương thức làm mới để làm mới sau một khoảng thời gian cố định, nhưng chức năng sentinel vẫn không hoạt động, cũng như cờ updateAll trên phương thức làm mới.


Bạn có thể chỉ cho tôi cách thức và nơi bạn truy cập cấu hình không? Tôi đã bắt chước tình huống của bạn trong một trong những dự án của riêng tôi và nó hoạt động hoàn hảo
Peter Bons

Tôi mong đợi một số ràng buộc cấu hình ở đâu đó trong ConfigureServicesphương thức của bạn trong startuop.cs, như services.Configure<LogSettings>(configuration.GetSection("LogSettings"));
Peter Bons

@peterBons liên kết của bạn đưa tôi đến 404.
Nick Gasia Robitsch

@PeterBons Tôi đã cập nhật bài đăng của mình để bao gồm thông tin được yêu cầu liên quan đến việc tiêm / ràng buộc cấu hình. Tôi đã không nghĩ rằng nó có liên quan tại thời điểm đó bởi vì nó đang hoạt động.
Nick Gasia Robitsch

1
Điều đó là vậy đó. Không có gì.
Peter Bons

Câu trả lời:


6

Ok, sau nhiều thử nghiệm và thử nghiệm và lỗi, tôi đã làm cho nó hoạt động.

Vấn đề của tôi là một dịch vụ còn thiếu cho phương thức cấu hình. Có một số hành vi thú vị ở đây, trong đó nó vẫn sẽ kéo xuống các cài đặt, nó sẽ không cập nhật, nếu điều này bị thiếu. Vì vậy, một khi điều này được đưa vào và với một sentinel thích hợp được định cấu hình cho mỗi tài liệu, nó sẽ hoạt động với cờ updateAll. Tuy nhiên điều này hiện chưa được ghi nhận.

Đây là giải pháp:

Trong Chương trình.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>();
    }
}

Sau đó, trong 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?}");
            });
        }
    }
}

Mô hình Cài đặt tôi đang ràng buộc dữ liệu đã lấy của mình vào:

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; }
    }
}

Một bộ điều khiển gia đình chung với cấu hình được đặt thành ViewBag để chuyển đến chế độ xem của chúng tôi:

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 });
        }
    }
}

Quan điểm của chúng tôi:

<!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>

Hy vọng điều này sẽ giúp người khác!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.