4

I have the following custom validation attribute, which derives from StringLengthAttribute:

public class StringLengthLocalizedAttribute : StringLengthAttribute
{
    public StringLengthLocalizedAttribute(int maximumLength) : base(maximumLength)
    {
        var translator = DependencyResolver.Current.GetService<ITranslator();
        var translatedValue = translator.Translate("MaxLengthTranslationKey", ErrorMessage);
        ErrorMessage = translatedValue.Replace("{MaxLength}", maximumLength.ToString());
    }
}

The only purpose of this custom attribute is to localize the ErrorMessage. The problem is, when I use this in my models it does not generate any client-side validation, but the standard StringLength attribute does.

I don't see how my attribute differs in any way - since it derives from the StringLength attribute I shouldn't have to implement any additional functionality to get client side validation working?

William
  • 733
  • 4
  • 10
  • 22

1 Answers1

3

If you look at the source code for DataAnnotationsModelValidatorProvider, you'll see in the method BuildAttributeFactoriesDictionary that specific types of attributes are registered for client side validation - you have created a new type, hence no client side validation.

Thankfully, this also has a public method to add your own adapter and is easy to use in the simple case you give:

Firstly, you need an adapter that will provide the client validation rules:

public class MyStringLengthAdapter : DataAnnotationsModelValidator<MyStringLengthAttribute>
{
    public MyStringLengthAdapter(ModelMetadata metadata, ControllerContext context, MyStringLengthAttribute attribute)
        : base(metadata, context, attribute)
    {
    }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        return new[] { new ModelClientValidationStringLengthRule(ErrorMessage, Attribute.MinimumLength, Attribute.MaximumLength) };
    }
}

You then need to register this in the Application_Start method in Global.asax.cs like so:

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof (MyStringLengthAttribute), typeof (MyStringLengthAdapter));
JonMac1374
  • 466
  • 2
  • 7
  • Well, that was embarassing... Just saw that I had already mapped my other custom localized attributes to their respective adapter counterparts in Application_Start during some earlier work with validation. – William Jan 28 '15 at 07:15
  • 1
    I might add that I didn't even have to create a custom MyStringLengthAdapter since I don't have any custom validation logic in my attribute. Mapping the adapter was enough: DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(StringLengthLocalizedAttribute), typeof(StringLengthAttributeAdapter)); – William Jan 28 '15 at 12:27