Sergey Rybalkin
Sergey Rybalkin

Reputation: 3026

Adding StringLengthAttribute through custom DataAnnotationsModelMetadataProvider

I have a requirement to add StringLengthAttribute validation to a lot of models in the existing ASP.NET MVC 4 project and am trying to do this automatically through my own model metadata provider derived from DataAnnotationsModelMetadataProvider.

It works perfectly with RequiredAttribute and some other data annotation attributes (I get both client-side and server-side validation working), however validation for the string length is not added - find the minimal example below.

public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes,
                                                    Type containerType,
                                                    Func<object> modelAccessor,
                                                    Type modelType,
                                                    string propertyName)
    {
        StringLengthAttribute lengthAttribute = new StringLengthAttribute(256);
        attributes = attributes.Union(new[] { lengthAttribute });
        return base.CreateMetadata(attributes,
                                   containerType,
                                   modelAccessor,
                                   modelType,
                                   propertyName);
    }
}

So, looks like StringLengthAttribute it being handled in some special way. Any ideas on how to make it work or a better implementation idea?

Upvotes: 1

Views: 1923

Answers (1)

Mike
Mike

Reputation: 2605

After playing around, I couldn't get the StringLength attribute to fire either.

Not ideal, but i guess an alternative solution is to the use a global ModelValidatorProvider class. Granted you won't get the built in Javascript that is provided by the StringLengthAttribute and you'd be writing your own logic, but its a possible quick fix for a problem you can solve later?

    public class MyCustomModelValidatorProvider : ModelValidatorProvider
{
    public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
    {
        return new List<ModelValidator>() { new MyCustomModelValidator(metadata, context) };
    }

    public class MyCustomModelValidator : ModelValidator
    {
        public MyCustomModelValidator(ModelMetadata metadata, ControllerContext context)
            : base(metadata, context)
        {   }

        public override IEnumerable<ModelValidationResult> Validate(object container)
        {
            var model = this.Metadata.Model; 
            if (model is string)
            {
                var value = model as string;

                if (String.IsNullOrEmpty(value) || value.Length > 256)
                {
                    var validationResult = new ModelValidationResult();
                    validationResult.Message = (this.Metadata.DisplayName ?? this.Metadata.PropertyName) 
                        + " needs to be no more then 256 characters";

                    return new List<ModelValidationResult>() { validationResult };
                }
            }

            return new List<ModelValidationResult>();
        }
    }

}

Add MyCustomModelValidator to the collection in Global.asax

        protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        // Use LocalDB for Entity Framework by default
        Database.DefaultConnectionFactory = new SqlConnectionFactory(@"Data Source=(localdb)\v11.0; Integrated Security=True; MultipleActiveResultSets=True");

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);


        ModelValidatorProviders.Providers.Add(new MyCustomModelValidatorProvider());
    }

Upvotes: 1

Related Questions