.NET Core 2.0数据注解本地化切换异常问题求助
解决.NET Core 2.0运行时切换语言后数据注解不更新的问题
这个问题我之前也碰到过,核心原因在于**.NET Core 2.0中数据注解的本地化元数据默认是全局缓存的**——模型元数据在第一次请求时就被创建并缓存,后续请求不会根据当前文化重新生成,所以切换语言后,数据注解的翻译还是用的缓存里的旧文化内容;而@Localizer["...."]是每次请求都会从资源文件里读取当前文化的对应值,所以能正常切换。
下面是具体的解决步骤:
1. 自定义模型元数据提供器,让缓存键包含当前文化
创建一个继承自DataAnnotationsModelMetadataProvider的类,重写相关方法,把当前请求的文化名称加入到缓存键中,这样不同文化下的模型元数据会被分别缓存:
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using System.Globalization; using System.Collections.Concurrent; public class CultureAwareDataAnnotationsModelMetadataProvider : DataAnnotationsModelMetadataProvider { protected override ModelMetadata CreateMetadata( IEnumerable<object> attributes, Type containerType, Type modelType, string propertyName) { var metadata = base.CreateMetadata(attributes, containerType, modelType, propertyName); // 将当前文化名称加入元数据附加信息,辅助缓存区分 metadata.AdditionalValues["Culture"] = CultureInfo.CurrentUICulture.Name; return metadata; } // 重写类型元数据获取方法,用包含文化的键缓存 public override ModelMetadata GetMetadataForType(Type modelType) { var cacheKey = $"{modelType.FullName}_{CultureInfo.CurrentUICulture.Name}"; if (ModelMetadataCache.TryGetValue(cacheKey, out var metadata)) { return metadata; } metadata = base.GetMetadataForType(modelType); ModelMetadataCache.TryAdd(cacheKey, metadata); return metadata; } // 重写属性元数据获取方法,用包含文化的键缓存 public override ModelMetadata GetMetadataForProperty(Type containerType, Type modelType, string propertyName) { var cacheKey = $"{containerType.FullName}.{propertyName}_{CultureInfo.CurrentUICulture.Name}"; if (ModelMetadataCache.TryGetValue(cacheKey, out var metadata)) { return metadata; } metadata = base.GetMetadataForProperty(containerType, modelType, propertyName); ModelMetadataCache.TryAdd(cacheKey, metadata); return metadata; } // 用线程安全字典实现文化区分的缓存 private static readonly ConcurrentDictionary<string, ModelMetadata> ModelMetadataCache = new ConcurrentDictionary<string, ModelMetadata>(); }
2. 在Startup.cs中替换默认的模型元数据提供器
在ConfigureServices方法里,注册我们自定义的提供器,替换掉默认的DataAnnotationsModelMetadataProvider:
public void ConfigureServices(IServiceCollection services) { // 配置本地化资源路径 services.AddLocalization(options => options.ResourcesPath = "Resources"); services.AddMvc() .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix) .AddDataAnnotationsLocalization() // 替换默认模型元数据提供器 .AddMvcOptions(options => { options.ModelMetadataDetailsProviders.Add(new CultureAwareDataAnnotationsModelMetadataProvider()); }); // 设置支持的文化列表 services.Configure<RequestLocalizationOptions>(options => { var supportedCultures = new[] { new CultureInfo("en"), new CultureInfo("fr") }; options.DefaultRequestCulture = new RequestCulture("en"); options.SupportedCultures = supportedCultures; options.SupportedUICultures = supportedCultures; }); }
3. 确保语言切换时正确更新当前文化
切换语言的动作要确保更新HttpContext的文化,并且把新的文化保存到Cookie里,这样后续请求会使用新的文化:
public IActionResult SetLanguage(string culture, string returnUrl) { Response.Cookies.Append( CookieRequestCultureProvider.DefaultCookieName, CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)), new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) } ); return LocalRedirect(returnUrl); }
额外注意点
- 如果你项目里用了
IMemoryCache,可以把自定义提供器里的字典缓存换成IMemoryCache,这样能更好地利用框架的缓存机制,也方便设置过期策略。 - 测试时要确认资源文件命名规范,比如模型的数据注解翻译文件应该是
Resources/YourModelName.fr.resx(对应法语),确保资源文件能被正确读取。
这样修改后,运行时切换语言时,数据注解的翻译就会跟着当前文化更新了——因为每个文化都有独立的模型元数据缓存,切换文化后会读取对应文化的缓存(如果没有就重新生成)。
内容的提问来源于stack exchange,提问作者user1961008
相关产品推荐
相关产品推荐

