ASP.NET MVC: Custom Validation by DataAnnotation
Solution 1
You could write a custom validation attribute:
public class CombinedMinLengthAttribute: ValidationAttribute
{
public CombinedMinLengthAttribute(int minLength, params string[] propertyNames)
{
this.PropertyNames = propertyNames;
this.MinLength = minLength;
}
public string[] PropertyNames { get; private set; }
public int MinLength { get; private set; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var properties = this.PropertyNames.Select(validationContext.ObjectType.GetProperty);
var values = properties.Select(p => p.GetValue(validationContext.ObjectInstance, null)).OfType<string>();
var totalLength = values.Sum(x => x.Length) + Convert.ToString(value).Length;
if (totalLength < this.MinLength)
{
return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}
and then you might have a view model and decorate one of its properties with it:
public class MyViewModel
{
[CombinedMinLength(20, "Bar", "Baz", ErrorMessage = "The combined minimum length of the Foo, Bar and Baz properties should be longer than 20")]
public string Foo { get; set; }
public string Bar { get; set; }
public string Baz { get; set; }
}
Solution 2
Self validated model
Your model should implement an interface IValidatableObject
. Put your validation code in Validate
method:
public class MyModel : IValidatableObject
{
public string Title { get; set; }
public string Description { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Title == null)
yield return new ValidationResult("*", new [] { nameof(Title) });
if (Description == null)
yield return new ValidationResult("*", new [] { nameof(Description) });
}
}
Please notice: this is a server-side validation. It doesn't work on client-side. You validation will be performed only after form submission.
Solution 3
ExpressiveAnnotations gives you such a possibility:
[Required]
[AssertThat("Length(FieldA) + Length(FieldB) + Length(FieldC) + Length(FieldD) > 50")]
public string FieldA { get; set; }
Solution 4
To improve Darin's answer, it can be bit shorter:
public class UniqueFileName : ValidationAttribute
{
private readonly NewsService _newsService = new NewsService();
public override bool IsValid(object value)
{
if (value == null) { return false; }
var file = (HttpPostedFile) value;
return _newsService.IsFileNameUnique(file.FileName);
}
}
Model:
[UniqueFileName(ErrorMessage = "This file name is not unique.")]
Do note that an error message is required, otherwise the error will be empty.
Solution 5
Background:
Model validations are required for ensuring that the received data we receive is valid and correct so that we can do the further processing with this data. We can validate a model in an action method. The built-in validation attributes are Compare, Range, RegularExpression, Required, StringLength. However we may have scenarios wherein we required validation attributes other than the built-in ones.
Custom Validation Attributes
public class EmployeeModel
{
[Required]
[UniqueEmailAddress]
public string EmailAddress {get;set;}
public string FirstName {get;set;}
public string LastName {get;set;}
public int OrganizationId {get;set;}
}
To create a custom validation attribute, you will have to derive this class from ValidationAttribute.
public class UniqueEmailAddress : ValidationAttribute
{
private IEmployeeRepository _employeeRepository;
[Inject]
public IEmployeeRepository EmployeeRepository
{
get { return _employeeRepository; }
set
{
_employeeRepository = value;
}
}
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
var model = (EmployeeModel)validationContext.ObjectInstance;
if(model.Field1 == null){
return new ValidationResult("Field1 is null");
}
if(model.Field2 == null){
return new ValidationResult("Field2 is null");
}
if(model.Field3 == null){
return new ValidationResult("Field3 is null");
}
return ValidationResult.Success;
}
}
Hope this helps. Cheers !
References
Related videos on Youtube
Danny van der Kraan
Blog: https://dannyvanderkraan.wordpress.com/ LinkedIn: https://nl.linkedin.com/in/dannyvanderkraan Tags: C#.NET, ASP.NET Core 1.0, Azure, NServiceBus, WPF MVVM, LINQ, Agile Scrum, TDD, DDD, UML ~ Let's help eachother become better developers.
Updated on April 26, 2020Comments
-
Danny van der Kraan about 4 years
I have a Model with 4 properties which are of type string. I know you can validate the length of a single property by using the StringLength annotation. However I want to validate the length of the 4 properties combined.
What is the MVC way to do this with data annotation?
I'm asking this because I'm new to MVC and want to do it the correct way before making my own solution.
-
levelnis about 11 yearsHave you looked at Fluent Validation? It handles complex scenarios much better than Data Annotations
-
Niks about 11 yearsTake a look at highly recommended solutions.... dotnetcurry.com/ShowArticle.aspx?ID=776
-
Danny van der Kraan about 11 yearsThanks for answering. I'll check out Fluent Validation, never heard of it. And Niks, Darin basically wrote out what the article at the link you posted explained. So, thank you... Awesome stuff!
-
-
Danny van der Kraan about 11 yearsThanks for answering, I accepted your answer. Feel a bit embarrassed actually. You wrote out the entire solution! Hehe. Only had to change the IsValid function to check for max length. So is this the accepted MVC solution for these types of problems?
-
Danny van der Kraan about 11 yearsThanks for answering Andrei. While your solution would work too I choose Darin's because it's more reusable.
-
Darin Dimitrov about 11 years@DannyvanderKraan, yes, this is the accepted way. Of course this sucks so badly that I never use it and use FluentValidation.NET instead to perform validation.
-
Danny van der Kraan about 11 yearsLol. Okok, I don't know what that is. Why is it better?
-
Darin Dimitrov about 11 yearsHere: fluentvalidation.codeplex.com. You could have just written a simple validator for the view model that might have looked like this (a single line of code):
this.RuleFor(x => x.Foo).Must((x, foo) => x.Foo.Length + x.Bar.Length + x.Baz.Length < 20).WithMessage("The combined minimum length of the Foo, Bar and Baz properties should be longer than 20");
. Now look at the code in my answer that you need to write with the data annotations and tell me which one you prefer. The declarative validation model is very poor compared to an imperative model. -
Danny van der Kraan about 11 yearsAgreed. Thank you. I'll look into Fluent Validation.
-
Data over 10 yearsyield return new ValidationResult("The title is mandatory.", "Title"); would add the property name, useful in grouping validation errors for display if necessary.
-
Jose over 10 yearsThis is a bit late, but does anyone know if there is a different setting that you have to "turn on" in order to allow custom data annotations? I know about adding a namespace for unobstrusive js on the web.config file, but anywhere else?
-
Grimm The Opiner over 10 yearsI've been looking for this all morning! I've mplemented it, and unfortunately when
IsValid
is called thevalidationContext
is null. Any idea what I did wrong? :-( -
Pedro almost 10 yearsNote that this validation method is only called after all validation attributes have passed validation.
-
IDIR Samir over 8 yearsThis is brilliant! my prayers got answered :)
-
Mou over 8 yearsdoes this kind of validation work at client side
CombinedMinLength()
required js will be injected for client side validation ? -
Darin Dimitrov over 8 years@Mou, for this to work you will need to write a corresponding client side extension to the standard validators because this is custom server side logic.
-
Steve S almost 8 yearsThis worked well for me since my validation was very specific. Adding a custom attribute would have been overkill for me since the validation was not going to be re-used.
-
Muhammad Ashikuzzaman over 6 years@DarinDimitrov thanks for this answer, But now if I hit a controller actoin having MyViewModel model; with ajax am seeing it is working in server side. But if I want to show the error message in client side when click on submit button what I have to do? please inform me.
-
Amol Jadhav almost 5 yearsThis is what I am looking for. Thank you!
-
Brad about 4 yearsJust found this answer and it's just saved loads of time. ExpressiveAnnotations are brilliant!