如何用ASP.Net Core 2.2与Westwind Globalization覆盖默认数据注解?
针对你遇到的问题——想用Westwind Globalization从数据库加载资源替换StringLength这类数据注解的默认提示,同时解决ModelMetadataDetailsProviders的缓存问题,我有几个亲测有效的方案,适配ASP.NET Core 2.2的特性:
1. 放弃ModelMetadataDetailsProviders,改用自定义ValidationAttributeAdapterProvider
ModelMetadataDetailsProviders的核心问题是启动时一次性生成并缓存模型元数据,后续语言切换或数据库资源更新都不会触发重新加载,完全不符合你动态从数据库取资源的需求。
替代方案是自定义ValidationAttributeAdapterProvider——这个组件会在每次验证请求时被调用,能动态获取最新的本地化消息,完美避开缓存陷阱。
实现自定义适配器提供者
创建一个继承自默认ValidationAttributeAdapterProvider的类,注入Westwind的IStringResourceManager,然后针对需要替换的注解(比如StringLength、Required)动态设置错误消息:
public class LocalizedValidationAdapterProvider : ValidationAttributeAdapterProvider { private readonly IStringResourceManager _resourceManager; // 注入Westwind的资源管理器 public LocalizedValidationAdapterProvider(IStringResourceManager resourceManager) { _resourceManager = resourceManager; } public override IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer) { // 处理StringLengthAttribute,支持占位符替换 if (attribute is StringLengthAttribute stringLengthAttr) { var localizedMsg = _resourceManager.GetString( "Validation_StringLength", new { Min = stringLengthAttr.MinimumLength, Max = stringLengthAttr.MaximumLength }); if (!string.IsNullOrEmpty(localizedMsg)) { stringLengthAttr.ErrorMessage = localizedMsg; } } // 处理RequiredAttribute(按需扩展Range、EmailAddress等其他注解) else if (attribute is RequiredAttribute requiredAttr) { var localizedMsg = _resourceManager.GetString("Validation_Required"); if (!string.IsNullOrEmpty(localizedMsg)) { requiredAttr.ErrorMessage = localizedMsg; } } // 调用默认逻辑完成适配器创建 return base.GetAttributeAdapter(attribute, stringLocalizer); } }
注册自定义适配器
在Startup.cs的ConfigureServices中,替换默认的适配器提供者,并配置Westwind使用数据库资源:
public void ConfigureServices(IServiceCollection services) { // 注册自定义验证适配器 services.AddSingleton<IValidationAttributeAdapterProvider, LocalizedValidationAdapterProvider>(); // 配置Westwind Globalization使用数据库 services.AddWestwindGlobalization(options => { options.ResourceAccessMode = ResourceAccessMode.DbResourceManager; options.ConnectionString = Configuration.GetConnectionString("YourDatabase"); options.ResourceTableName = "AppLocalizationResources"; // 你的数据库资源表名 options.ResourceBaseName = "YourAppResources"; // 对应数据库中的ResourceSet字段 options.AddMissingResources = false; // 禁止自动添加缺失资源(按需设置) }); // 其他Mvc配置 services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); }
2. 处理语言切换后的资源缓存
Westwind默认会缓存资源以提升性能,所以在切换语言时,需要主动清除缓存,确保下次请求能读取最新的数据库资源:
public IActionResult SwitchLanguage(string culture) { // 设置语言Cookie Response.Cookies.Append( CookieRequestCultureProvider.DefaultCookieName, CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)), new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) } ); // 清除Westwind的资源缓存 var resourceManager = HttpContext.RequestServices.GetRequiredService<IStringResourceManager>(); resourceManager.ClearResourceCache(); return Redirect(Request.Headers["Referer"].ToString()); }
3. 数据库资源表的注意事项
确保你的Westwind资源表中存在对应的键值对,比如:
- 键名:
Validation_StringLength,值:"字段长度必须在{Min}到{Max}字符之间" - 键名:
Validation_Required,值:"此字段为必填项"
Westwind会自动根据当前文化(Culture)加载对应的资源行,无需手动处理多语言分支。
这种方案的核心是把消息的获取延迟到每次验证请求时,彻底避开模型元数据的缓存问题,同时完全利用Westwind Globalization的数据库资源管理能力,完美匹配你的需求。
内容的提问来源于stack exchange,提问作者David Hendrick

