ASP.NET MVC 3 Data Annotation: Add validation dynamically
Solution 1
I think that the simplest way of doing what I wanted is implementing IValidatableObject
:
public class Product : IValidatableObject
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Prop1 < Prop2)
yield return new ValidationResult("Property 1 can't be less than Property 2");
}
}
See also: Class-Level Model Validation with ... ASP.NET MVC 3
Solution 2
Custom Attribute:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class CustomRequiredIfAttribute : CustomAttribute
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public CustomRequiredIfAttribute()
{
}
public CustomRequiredIfAttribute(string dependentProperty, object targetValue)
: base()
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
}
Custom RequiredIfValidator
using System;
using System.Collections.Generic;
using System.Web.Mvc;
namespace Custom.Web.Validation
{
public class RequiredIfValidator : DataAnnotationsModelValidator<CustomRequiredIfAttribute>
{
public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, CustomRequiredIfAttribute attribute)
: base(metadata, context, attribute)
{
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
return base.GetClientValidationRules();
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
// get a reference to the property this validation depends upon
var field = Metadata.ContainerType.GetProperty(Attribute.DependentProperty);
if (field != null)
{
// get the value of the dependent property
object value = field.GetValue(container, null);
// compare the value against the target value
if (this.IsEqual(value) || (value == null && Attribute.TargetValue == null))
{
// match => means we should try validating this field
if (!Attribute.IsValid(Metadata.Model))
{
// validation failed - return an error
yield return new ModelValidationResult { Message = ErrorMessage };
}
}
}
}
private bool IsEqual(object dependentPropertyValue)
{
bool isEqual = false;
if (Attribute.TargetValue != null && Attribute.TargetValue.GetType().IsArray)
{
foreach (object o in (Array)Attribute.TargetValue)
{
isEqual = o.Equals(dependentPropertyValue);
if (isEqual)
{
break;
}
}
}
else
{
isEqual = Attribute.TargetValue.Equals(dependentPropertyValue);
}
return isEqual;
}
}
}
Register custom DataAnnotationsModelValidatorProvider
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(CustomRequiredIfAttribute), typeof(RequiredIfValidator));
Use this CustomRequiredIf in the ViewModel
[CustomRequiredIf("CategoryId", 3, ErrorMessageResourceName = GlobalResourceLiterals.AccountGroup_Required)]
public string AccountGroup { get; set; }
Solution 3
Heres the updated MVC 3 version of that blog post http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx
Related videos on Youtube
![Diego](https://i.stack.imgur.com/WLBKa.jpg?s=256&g=1)
Diego
I'm 28 years old and I started working as a developer as soon as I graduated ORT high-school technical course of studies. I've been working in software development ever since and I have acquired experience with different kind of teams and projects. I've also worked with some clients on my own as a freelancer. Since the beginning of my tech career, I've wanted to grow as a software architect and I'm proud to say I have. I've mainly worked with .NET web technologies and been in charge of different projects, which led me to becoming the architect of a very large and complex product. After getting my Systems Analyst degree I found a new passion in Information Security, so I studied an Information Security Master's degree at Universidad de Buenos Aires. Although I'm not sure I'd like to work directly at the Security area, I love applying that knowledge to development and I really think that's an underrated profile. Besides being kind of a geek and a techie, I've trained Taekwon-do for a lot of years and I enjoy playing sports like handball, tennis and squash. I also like cooking and, of course, eating!
Updated on July 09, 2022Comments
-
Diego almost 2 years
I'm new with data annotation. I'd like to know if it possible (and how) to add some validation dynamically. It is very extensive to explain why, but I've a ViewModel that receives and object when created. In that object I must check for some property and depending its value I should have or not some validations.
An example:
public class ProfileViewModel { [Required(ErrorMessage = "The field {0} is required")] [Display(Name = "Client Code")] public int ClientCode { get; set; } [Required(ErrorMessage = "The field {0} is required")] [StringLength(100, ErrorMessage = "The field {0} must have up to 100 characters.")] [Display(Name = "Company")] public string Company { get; set; } [StringLength(50, ErrorMessage = "The field {0} must have up to 50 characters.")] [Display(Name = "Name")] public string Name { get; set; } [StringLength(50, ErrorMessage = "The field {0} must have up to 50 characters.")] [Display(Name = "LastName")] public string LastName { get; set; } public ProfileViewModel(User usr) { if (usuario.ClientCode != null) { ClientCode = Convert.ToInt32(usr.ClientCode); } else { //ClientCode and Company are not yet required. //Name and LastName are now required. } Company = usr.Company; Name = usr.Name; LastName = usr.LastName; } }
-
xr280xr about 9 yearsThe problem is, this doesn't add client-side validation. So if you are already using client-side validation based on attributes, this causes an inconsistent user experience. MS got it all wrong tying validation to attributes.
-
Diego about 9 yearsYou are right. This doesn't add client-side validation. If you need it I believe the best way would be to write your own script to add this validation.