Unfortunately MVC3 doesn’t respect the [Required]
attribute’s AllowEmptyStrings
property. When using a class both as an MVC model and as an EF Code First entity this is a problem. What if I want to allow empty strings, but not null
values in the database?
The problem lies in the client side validation performed by MVC3. If a property in the model is marked with [Required]
the jquery required validator will be enabled. It requires a non-empty string. I would prefer MVC to not emit a required validation if AllowEmptyStrings
is true. Unfortunately the MVC code doesn’t honor that flag, but there is a workaround. Create a small attribute derived from RequiredAttribute
.
public sealed class NotNullAttribute : RequiredAttribute { public NotNullAttribute() { AllowEmptyStrings = true; } } |
To make it work, another attribute has to be set on the property to prevent the model binder from converting an empty string to null
.
[NotNull] [DisplayFormat(ConvertEmptyStringToNull = false)] public string SomeProperty { get; set; } |
It is surprisingly simple, but works thanks to a difference in how EF Code First and MVC3 handles attributes.
EF Code First recognizes the code above as a RequiredAttribute
and sets the column in the database to not null
accordingly. That is te normal behavior when querying for attributes – check if the attribute is of a given class, or any class derived from it. What is a bit surprising is that the MVC3 client validation does something else.
To understand why we first have to catch up on client side handling of validation attributes. When writing a custom validation attribute, client side validation is handled by implementing the IClientValidatable
interface. However, the built in attributes belong to the System.ComponentModel.DataAnnotations
and are not aware of client validation in MVC. For the built in attributes, there is a bit of special handling in MVC3 to give them client validation. The relevant part of the code is in DataAnnotationsModelValidatorProvider.cs
:
84 85 86 87 88 89 90 91 | // Produce a validator for each validation attribute we find foreach (ValidationAttribute attribute in attributes.OfType<ValidationAttribute>()) { DataAnnotationsModelValidationFactory factory; if (!AttributeFactories.TryGetValue(attribute.GetType(), out factory)) { factory = DefaultAttributeFactory; } results.Add(factory(metadata, context, attribute)); } |
The code checks for all attributes derived from ValidationAttribute
. It then looks up the type of the attribute in a dictionary, to get a factory instance. That lookup is done on the exact type, not checking derived types. I don’t know if this is intentional or not, but it provides an excellent extension point. If a RequiredAttribute
is passed in, a special factory producing a client side required rule is returned. For our NotNullAttribute
there is no match so a DefaultAttributeFactory
is returned. It will eventually produce client validation rules for any attribute that implements IClientValidatable
. The NotNullAttribute
doesn’t, so no client rules are created.
In a future version of MVC I would prefer if the RequiredAttributeAdapter
checked the AllowEmptyStrings
property before emitting the client validation rule. Until then, the small NotNullAttribute
provides a workaround.