Default resource for data annotations in ASP.NET MVC
Asked Answered
M

3

19

There's a way to set the default resource to the data annotations validations?

I don't wanna make something like this:

[Required(ErrorMessage="Name required.", ErrorMessageResourceType=typeof(CustomDataAnnotationsResources)]
public string Name { get; set; }

I would like something like this:

Global.asax

DataAnnotations.DefaultResources = typeof(CustomDataAnnotationsResources);

then

[Required]
public string Name { get; set; }

someone gimme a light!

thanks in advance

EDIT

My real problem was with EF Code First CTP4. CTP5 fix it. Thanks for everyone.

Martres answered 15/7, 2010 at 23:4 Comment(2)
Can somebody please shed light as to whether this is possible and how to approach this. Thanks.Dramatist
have a look at adamyan.blogspot.be/2010/02/… you can find the list of default messages here: https://mcmap.net/q/638350/-where-is-the-whole-list-of-default-error-messages-for-dataannotations-at-mvc-3Serviette
C
13

You could try doing this:

Add this class somewhere in your project:

 public class ExternalResourceDataAnnotationsValidator : DataAnnotationsModelValidator<ValidationAttribute>
{
    /// <summary>
    /// The type of the resource which holds the error messqages
    /// </summary>
    public static Type ResourceType { get; set; }

    /// <summary>
    /// Function to get the ErrorMessageResourceName from the Attribute
    /// </summary>
    public static Func<ValidationAttribute, string> ResourceNameFunc 
    {
        get { return _resourceNameFunc; }
        set { _resourceNameFunc = value; }
    }
    private static Func<ValidationAttribute, string> _resourceNameFunc = attr => attr.GetType().Name;

    public ExternalResourceDataAnnotationsValidator(ModelMetadata metadata, ControllerContext context, ValidationAttribute attribute)
        : base(metadata, context, attribute)
    {
        if (Attribute.ErrorMessageResourceType == null)
        {
            this.Attribute.ErrorMessageResourceType = ResourceType;
        }

        if (Attribute.ErrorMessageResourceName == null)
        {
            this.Attribute.ErrorMessageResourceName = ResourceNameFunc(this.Attribute);
        }
    }
}

and in your global.asax, add the following:

// Add once
ExternalResourceDataAnnotationsValidator.ResourceType = typeof(CustomDataAnnotationsResources);

// Add one line for every attribute you want their ErrorMessageResourceType replaced.
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RangeAttribute), typeof(ExternalResourceDataAnnotationsValidator));

It will look for a property with the same name as the validator type for the error message. You can change that via the ResourceNameFunc property.

EDIT: AFAIK this works from MVC2 onwards, as DataAnnotationsModelValidatorProvider was introduced in MVC2.

Cameo answered 3/12, 2010 at 1:24 Comment(8)
@CGK: Please also take a look at this question.#4301718. I have explained my requirements there although I have placed a bounty on this question.Dramatist
@conqenator: Answered there too.Cameo
How to use this solution for changing the default message: "The value '{0}' is not valid for {1}.", raised when the data the user entered isn't compatible with the data type. This is default validation - there is no need for explicit adding any attribute for data type validation, so how to localize the default error message ?Prado
This solution does not cover that problem. You can look at the following link for instructions on how to do that: https://mcmap.net/q/263356/-providing-localized-error-messages-for-non-attributed-model-validation-in-asp-net-mvc-2/525499Cameo
@CGK: I've seen that but the only problem is that I have my resources in separate assembly: #5091251Prado
AFAIK that doesn't work with resources on a separate assembly.Cameo
I thought this solution was working, but it completely breaks client side validation, so I do not recommend it. Instead, it is possible to go along these lines and override the default DataAnnotationsModelValidatorProvider. I did it and intercepted the attributes very easily, and I don't have to register the adapter for each validation attribute anymore. It's still possible to use this solution, thought you will have to inherit each default adapter instead of the base adapter class, to maintain client side validation working.Aswan
@Aswan Great addition. The answer provided handled cases before MVC provided client side validation if I remember correctly.Cameo
C
12

To achieve this, I created a new class that inherits from RequiredAttribute, and the error message is embedded inside this new class:

public class RequiredWithMessageAttribute : RequiredAttribute
{
    public RequiredWithMessageAttribute()
    {
        ErrorMessageResourceType = typeof(ValidationResource);
        ErrorMessageResourceName = "RequiredErrorMessage";
    }
}

The error message is taken from the ValidationResource.resx file, where I list the error message as follows:

RequiredErrorMessage --> "{0} is required."

where {0} = display name.

I then annotate my models like this, so I never have to repeat my error message declarations:

[RequiredWithMessage]
public string Name { get; set; }

Once you do this, an error message ("Name is required.") will appear whenever validation fails.

This works properly with ASP.NET MVC's server-side validation and client-side validation.

Chromomere answered 29/11, 2010 at 6:49 Comment(2)
It doesn't work!!! This method works for "DisplayNameAttribute" for example but it doesn't work for "RequiredAttribute". That drives me crazy :( I don't know what to do.Reify
It works only on server side because it dont generate data-val-required html attribute in MVC5Neldanelia
T
3

I did another approach. It still needs you to inherit DataAnnotation attributes, but you can get a more flexible translation solution.

Code from my blog post (read it fore more details)

End result

public class User
{
    [Required]
    [LocalizedDisplayNameAttribute("User_Id")]
    public int Id { get; set; }

    [Required]
    [StringLength(40)]
    [LocalizedDisplayNameAttribute("User_FirstName")]
    public string FirstName { get; set; }

    [Required]
    [StringLength(40)]
    [LocalizedDisplayNameAttribute("User_LastName")]
    public string LastName { get; set; }
}

1 Inherit all data annotation attributes like this

public class RequiredAttribute : System.ComponentModel.DataAnnotations.RequiredAttribute
{
    private string _displayName;

    public RequiredAttribute()
    {
        ErrorMessageResourceName = "Validation_Required";
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        _displayName = validationContext.DisplayName;
        return base.IsValid(value, validationContext);
    }

    public override string FormatErrorMessage(string name)
    {
        var msg = LanguageService.Instance.Translate(ErrorMessageResourceName);
        return string.Format(msg, _displayName);
    }
}

2 Inherit DisplayNameAttribute

public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
    private PropertyInfo _nameProperty;
    private Type _resourceType;

    public LocalizedDisplayNameAttribute(string className, string propertyName)
        : base(className + (propertyName == null ? "_Class" : ("_" + propertyName)))
    {

    }

    public override string DisplayName
    {
        get
        {
            return LanguageService.Instance.Translate(base.DisplayName) ?? "**" + base.DisplayName + "**";
        }
    }
}

3. Create the language service (you can switch to any language source in it)

public class LanguageService
{
    private static LanguageService _instance = new LanguageService();
    private List<ResourceManager> _resourceManagers = new List<ResourceManager>();

    private LanguageService()
    {
    }

    public static LanguageService Instance { get { return _instance; } }

    public void Add(ResourceManager mgr)
    {
        _resourceManagers.Add(mgr);
    }

    public string Translate(string key)
    {
        foreach (var item in _resourceManagers)
        {
            var value = item.GetString(key);
            if (value != null)
                return value;
        }

        return null;
    }
}

Finally you need to register the string tables you use to translate the validation messages and your models

LanguageService.Instance.Add(MyNameSpace.ModelResource.ResourceManager);
LanguageService.Instance.Add(MyNameSpace.ValidationResources.ResourceManager);
Tallis answered 3/12, 2010 at 13:13 Comment(1)
+1 for sharing all that code. I have yet to figure out quite how to use it (LoL) but it seems it will solve my problemJocosity

© 2022 - 2024 — McMap. All rights reserved.