DataAnnotation with custom ResourceProvider

后端 未结 3 1363
有刺的猬
有刺的猬 2020-12-28 16:43

I have created a custom ResourceProvider to pull localization information from a database. I now want to use DataAnnotation to add

相关标签:
3条回答
  • 2020-12-28 17:01

    I realize this is an old question, but wanted to add a bit. I found myself in the same situation and there doesn't appear to be any documentation/blogumentation on this topic. Nevertheless, I figured out a way to use a custom resource provider, with one caveat. The caveat is that I'm in an MVC application so I still have HttpContext.GetLocalResourceObject() available. This is the method that asp.net uses to localize items. The absence of the resource object doesn't stop you from writing our own solution, even if its a direct query of the DB tables. Nevertheless, I thought it was worth pointing out.

    While I'm not terribly happy with the following solution, it seems to work. For each validation attribute I want to use I inherit from said attribute and overload the IsValid(). The decoration looks like this:

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

    The new attribute looks like this:

    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);
        }
    }
    

    Notes

    • You need to decorate your code with the derived attribute, not the standard one
    • I'm using ErrorMessageResourceType to pass the type of the class being validated. By that I mean if I'm in a customer class and validating the FirstName property I would pass typeof(customer). I'm doing this because in my database backend I'm using the full class name (namespace + classname) as a key (the same way a page URL is used in asp.net).
      • MVC_HtmlHelpers.Localize is just a simple wrapper for my custom resource provider

    The (semi-stolen) helper code looks like this ....

    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;
        }
    }
    
    0 讨论(0)
  • 2020-12-28 17:13

    I have used fluent validation to achieve this. It saves me lots of time. This is what my Globalized validator looks like. It does mean that you don't use data anotations, but sometimes data anotations get a bit big and messy.

    Here is an example:

    (Errors.Required, Labels.Email and Errors.AlreadyRegistered are in my blobal resources folder.)

    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?
            ...
        }    
    }
    

    Like I said, it is a move away form data annotations, only because I already have too many annotations on my methods already!

    0 讨论(0)
  • 2020-12-28 17:20

    I'll add my findings since I had to fight with this. Maybe it will help someone.

    When you derive from RequiredAttribute, it seems to break client side validation. So to fix this I implemented IClientValidatable and implemented the GetClientValidationRules method. Resources.GetResources is static helper method I have that wraps around HttpContext.GetGlobalResourceObject.

    The custom required attribute:

    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"
            };
        }
    }
    

    Usage:

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

    And my Resources helper if anyone is interested:

    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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题