.NET运行时更新RequestLocalizationOptions无需重启应用
解决方案:.NET运行时动态更新RequestLocalizationOptions
要实现运行时新增文化后无需重启应用更新本地化配置,核心是解决启动时一次性加载的问题,通过缓存+动态选项更新的方式实现。以下是具体步骤:
1. 修复启动时异步配置的潜在问题
原代码中services.Configure<RequestLocalizationOptions>使用异步委托是无效的(Configure仅接受同步Action),会导致启动时数据库查询未完成就继续初始化,可能出现初始文化列表为空的情况。我们改为用文化管理服务统一处理文化加载逻辑。
2. 实现带缓存的文化管理服务
创建ICultureManager及其实现类,结合内存缓存避免频繁查询数据库,同时支持手动刷新缓存:
public interface ICultureManager { Task<IList<CultureInfo>> GetSupportedCulturesAsync(); void RefreshCultures(); } public class CultureManager : ICultureManager { private readonly ICultureService _cultureService; private readonly IMemoryCache _cache; private const string _cacheKey = "SupportedCultures"; private readonly TimeSpan _cacheExpiration = TimeSpan.FromMinutes(5); // 可根据需求调整缓存时长 public CultureManager(ICultureService cultureService, IMemoryCache cache) { _cultureService = cultureService; _cache = cache; } public async Task<IList<CultureInfo>> GetSupportedCulturesAsync() { return await _cache.GetOrCreateAsync(_cacheKey, async entry => { entry.AbsoluteExpirationRelativeToNow = _cacheExpiration; var dbCultures = await _cultureService.GetFromDatabase(x => x.Available == true); return dbCultures.Select(c => new CultureInfo($"{c.language}-{c.CountryCodeIso2}")).ToList(); }); } public void RefreshCultures() { _cache.Remove(_cacheKey); } }
3. 重构本地化选项配置
修改AddRequestLocalizationOptions扩展方法,注册文化管理服务,并初始化基础配置:
public static IServiceCollection AddRequestLocalizationOptions(this IServiceCollection services) { // 注册文化管理服务 services.AddScoped<ICultureManager, CultureManager>(); // 注册内存缓存 services.AddMemoryCache(); // 初始化RequestLocalizationOptions基础配置 services.Configure<RequestLocalizationOptions>(options => { options.DefaultRequestCulture = new RequestCulture("en-US"); options.RequestCultureProviders = new List<IRequestCultureProvider> { new CookieRequestCultureProvider { CookieName = CookieDefaults.CultureKey } }; }); return services; }
4. 启动时加载初始文化列表
在Program.cs中,应用启动后立即加载初始文化并更新选项:
var app = builder.Build(); // 初始化初始文化列表 using var scope = app.Services.CreateScope(); var cultureManager = scope.ServiceProvider.GetRequiredService<ICultureManager>(); var initialCultures = await cultureManager.GetSupportedCulturesAsync(); var optionsMonitor = scope.ServiceProvider.GetRequiredService<IOptionsMonitor<RequestLocalizationOptions>>(); var currentOptions = optionsMonitor.CurrentValue; currentOptions.SupportedCultures = initialCultures; currentOptions.SupportedUICultures = initialCultures; // 启用本地化中间件 app.UseRequestLocalization(); // 其他中间件配置... app.Run();
5. 实现运行时刷新接口
添加API接口,用于手动触发文化更新(比如新增文化后调用此接口):
[ApiController] [Route("api/culture")] public class CultureController : ControllerBase { private readonly ICultureManager _cultureManager; private readonly IOptionsMonitor<RequestLocalizationOptions> _optionsMonitor; public CultureController(ICultureManager cultureManager, IOptionsMonitor<RequestLocalizationOptions> optionsMonitor) { _cultureManager = cultureManager; _optionsMonitor = optionsMonitor; } [HttpPost("refresh")] public async Task<IActionResult> RefreshCultures() { // 清除缓存,重新从数据库加载 _cultureManager.RefreshCultures(); var latestCultures = await _cultureManager.GetSupportedCulturesAsync(); // 更新RequestLocalizationOptions var currentOptions = _optionsMonitor.CurrentValue; currentOptions.SupportedCultures = latestCultures; currentOptions.SupportedUICultures = latestCultures; return Ok("支持文化列表已更新"); } }
6. 可选:添加文化验证逻辑(避免无效文化)
如果需要确保请求的文化始终在支持列表中,可以自定义RequestCultureProvider,在每次请求时验证:
public class ValidatingCultureProvider : IRequestCultureProvider { private readonly ICultureManager _cultureManager; public ValidatingCultureProvider(ICultureManager cultureManager) { _cultureManager = cultureManager; } public async Task<ProviderCultureResult?> DetermineProviderCultureResult(HttpContext httpContext) { var cookieCulture = httpContext.Request.Cookies[CookieDefaults.CultureKey]; if (string.IsNullOrEmpty(cookieCulture)) return null; var supportedCultures = await _cultureManager.GetSupportedCulturesAsync(); if (!supportedCultures.Any(c => c.Name.Equals(cookieCulture, StringComparison.OrdinalIgnoreCase))) { // 若请求的文化不在支持列表中,返回默认文化 return new ProviderCultureResult("en-US"); } return null; // 交由后续提供器处理 } }
然后在配置RequestLocalizationOptions时添加此提供器:
services.Configure<RequestLocalizationOptions>(options => { options.DefaultRequestCulture = new RequestCulture("en-US"); options.RequestCultureProviders = new List<IRequestCultureProvider> { new CookieRequestCultureProvider { CookieName = CookieDefaults.CultureKey }, new ValidatingCultureProvider(scope.ServiceProvider.GetRequiredService<ICultureManager>()) }; });
内容的提问来源于stack exchange,提问作者mihai921116
相关产品推荐
相关产品推荐

