代码之家  ›  专栏  ›  技术社区  ›  Zak

使用自定义资源提供程序的DataAnnotation

  •  19
  • Zak  · 技术社区  · 15 年前

    我创造了一个 习俗 ResourceProvider 从数据库中提取本地化信息。我现在想用 DataAnnotation 向模型添加验证。

    数据批注 ErrorMessageResourceType ErrorMessageResourceName 财产,但是 错误消息资源类型 只接受 System.Type (即编译的资源文件)

    有什么方法可以让dataannotation使用自定义resourceprovider吗?

    3 回复  |  直到 11 年前
        1
  •  6
  •   EBarr labilbe    13 年前

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

    虽然我对下面的解决方案不太满意,但它似乎奏效了。对于我要使用的每个验证属性,我从所述属性继承并重载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传递正在验证的类的类型。我的意思是,如果我在一个客户类中,验证 第一名字 我会通过的财产 类型(客户) . 我这样做是因为在我的数据库后端,我使用完整的类名(命名空间+类名)作为键(就像在asp.net中使用页面url一样)。
      • mvc_htmlhelpers.本地化 只是我的自定义资源提供程序的一个简单包装

    (半被盗的)帮助程序代码如下所示……

    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;
        }
    }
    
        2
  •  3
  •   Dai Bok    15 年前

    我使用了fluent验证来实现这一点。它节省了我很多时间。这就是我的全球化验证器的样子。这确实意味着你不使用数据变异,但有时数据变异变得有点大和混乱。

    下面是一个例子:

    (errors.required、labels.email和errors.alreadyRegistered位于我的blobal resources文件夹中。)

    public class CreateEmployerValidator : AbstractValidator<CreateEmployerModel> {
        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?
            ...
        }    
    }
    

    就像我说的,这是一个移动表单数据注释,只是因为我已经有太多的注释在我的方法上了!

        3
  •  2
  •   Chad    13 年前

    我会补充我的发现,因为我不得不与此抗争。也许它能帮助别人。

    从RequiredAttribute派生时,似乎会中断客户端验证。为了解决这个问题,我实现了iclientvaliddatable并实现了getclientvalidationrules方法。resources.getresources是我在httpcontext.getglobalresourceobject周围使用的静态帮助器方法。

    自定义必需属性:

    public class LocalizedRequiredAttribute : RequiredAttribute, IClientValidatable 
    {
        public LocalizedRequiredAttribute(string resourceName)
        {
            this.ErrorMessage = Resources.GetResource(resourceName);
        }
    
        public IEnumerable<ModelClientValidationRule> 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;
        }
    }