【问题标题】:DataAnnotation with custom ResourceProvider带有自定义 ResourceProvider 的 DataAnnotation
【发布时间】:2009-12-04 17:16:45
【问题描述】:

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

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

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

【问题讨论】:

    标签: c# asp.net-mvc localization resources


    【解决方案1】:

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

    虽然我对以下解决方案不太满意,但它似乎有效。对于我想使用的每个验证属性,我都从所述属性继承并重载 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 来传递正在验证的类的类型。我的意思是,如果我在客户类中并验证 FirstName 属性,我将通过 typeof(customer)。我这样做是因为在我的数据库后端中,我使用完整的类名(命名空间 + 类名)作为键(与在 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;
        }
    }
    

    【讨论】:

      【解决方案2】:

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

      这是一个例子:

      (Errors.Required、Labels.Email 和 Errors.AlreadyRegistered 在我的全局资源文件夹中。)

      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】:

        我将添加我的发现,因为我不得不与此作斗争。也许它会帮助某人。

        当您从RequiredAttribute 派生时,它似乎会破坏客户端验证。所以为了解决这个问题,我实现了 IClientValidatable 并实现了 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;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-04-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多