DataAnnotation与自定义ResourceProvider

我创建了一个自定义ResourceProvider来从数据库中提取本地化信息。 我现在想使用DataAnnotation为模型添加validation。

DataAnnotation具有ErrorMessageResourceTypeErrorMessageResourceName属性,但ErrorMessageResourceType仅接受System.Type (即已编译的资源文件)

有没有办法让DataAnnotation使用自定义ResourceProvider?

我意识到这是一个老问题,但想补充一点。 我发现自己处于同样的情况,似乎没有关于这个主题的任何文档/博客。 尽管如此,我想出了一种使用自定义资源提供程序的方法,但有一点需要注意。 需要注意的是,我在MVC应用程序中,因此我仍然可以使用HttpContext.GetLocalResourceObject() 。 这是asp.net用于本地化项目的方法。 缺少资源对象并不会阻止您编写我们自己的解决方案,即使它是对DB表的直接查询。 不过,我认为值得指出。

虽然我对以下解决方案并不十分满意,但它似乎有效。 对于我想要使用的每个validation属性,我从所述属性inheritance并重载IsValid()。 装饰看起来像这样:

 [RequiredLocalized(ErrorMessageResourceType= typeof(ClassBeginValidated), ErrorMessageResourceName="Errors.GenderRequired")] public string FirstName { get; set; } 

新属性如下所示:

 public sealed class RequiredLocalized : RequiredAttribute { public override bool IsValid(object value) { if ( ! (ErrorMessageResourceType == null || String.IsNullOrWhiteSpace(ErrorMessageResourceName) ) ) { this.ErrorMessage = MVC_HtmlHelpers.Localize(this.ErrorMessageResourceType, this.ErrorMessageResourceName); this.ErrorMessageResourceType = null; this.ErrorMessageResourceName = null; } return base.IsValid(value); } } 

笔记

  • 您需要使用派生属性而不是标准属性来装饰代码
  • 我正在使用ErrorMessageResourceType传递要validation的类的类型。 我的意思是,如果我在客户类并validationFirstName属性,我将传递typeof(客户) 。 我这样做是因为在我的数据库后端我使用完整的类名(namespace + classname)作为键(与asp.net中使用页面URL的方式相同)。
    • MVC_HtmlHelpers.Localize只是我的自定义资源提供程序的一个简单包装器

(半被盗)助手代码看起来像这样….

 public static string Localize (System.Type theType, string resourceKey) { return Localize (theType, resourceKey, null); } public static string Localize (System.Type theType, string resourceKey, params object[] args) { string resource = (HttpContext.GetLocalResourceObject(theType.FullName, resourceKey) ?? string.Empty).ToString(); return mergeTokens(resource, args); } private static string mergeTokens(string resource, object[] args) { if (resource != null && args != null && args.Length > 0) { return string.Format(resource, args); } else { return resource; } } 

我已经使用流畅的validation来实现这一目标。 它节省了我很多时间。 这就是我的Globalizedvalidation器的样子。 它确实意味着您不使用数据anotations,但有时数据anotations有点大而且混乱。

这是一个例子:

(Errors.Required,Labels.Email和Errors.AlreadyRegistered在我的blobal资源文件夹中。)

 public class CreateEmployerValidator : AbstractValidator { public RegisterUserValidator() { RuleFor(m => m.Email) .NotEmpty() .WithMessage(String.Format(Errors.Required, new object[] { Labels.Email })) .EmailAddress() .WithMessage(String.Format(Errors.Invalid, new object[] { Labels.Email })) .Must(this.BeUniqueEmail) .WithMessage(String.Format(Errors.AlreadyRegistered, new object[] { Labels.Email })); } public bool BeUniqueEmail(this IValidator validator, string email ) { //Database request to check if email already there? ... } } 

就像我说的那样,这是一种从数据注释中移开的方式,只是因为我的方法已经有太多的注释了!

我将添加我的发现,因为我必须与此斗争。 也许它会帮助别人。

当您从RequiredAttribute派生时,它似乎打破了客户端validation。 所以为了解决这个问题,我实现了IClientValidatable并实现了GetClientValidationRules方法。 Resources.GetResources是我的包含HttpContext.GetGlobalResourceObject的静态帮助器方法。

自定义必需属性:

 public class LocalizedRequiredAttribute : RequiredAttribute, IClientValidatable { public LocalizedRequiredAttribute(string resourceName) { this.ErrorMessage = Resources.GetResource(resourceName); } public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { yield return new ModelClientValidationRule { ErrorMessage = this.ErrorMessage, ValidationType= "required" }; } } 

用法:

 [LocalizedRequired("SomeResourceName")] public string SomeProperty { get; set; } 

如果有人有兴趣,我的资源帮助:

 public class Resources { public static string GetResource(string resourceName) { string text = resourceName; if (System.Web.HttpContext.Current != null) { var context = new HttpContextWrapper(System.Web.HttpContext.Current); var globalResourceObject = context.GetGlobalResourceObject(null, resourceName); if (globalResourceObject != null) text = globalResourceObject.ToString(); } return text; } }