Probably I am missing something, but having the model below
public class MyModel
{
public double WhateverButNotZero { get; set; }
}
is there any MVC built-in DataAnnotation to validate the number as "everything but zero"?
Regex to the rescue:
public class MyModel
{
[RegularExpression("(.*[1-9].*)|(.*[.].*[1-9].*)")]
public double WhateverButNotZero { get; set; }
}
There is no build-in validation for that specifically, but you can create a custom attribute for it if you don't want to use regex as mentioned in other answers.
Create a class that extends the ValidationAttribute class
Override the IsValid(object value) method
Inside your validation method, convert the object to int and check if it's equal zero
public class NotZeroAttribute : ValidationAttribute
{
public override bool IsValid(object value) => (int)value != 0;
}
Then just use it on your class property like that:
public class MyModel
{
[NotZero]
public double WhateverButNotZero { get; set; }
}
try using regex annotation
public class MyModel
{
[RegularExpression("^(?!0*(\.0+)?$)(\d+|\d*\.\d+)$", ErrorMessage = "Not Equal to Zero")]
public double WhateverButNotZero { get; set; }
}
You can use RegularExpression DataAnnotation attribute.
[RegularExpression(#"^\d*[1-9]\d*$")]
public double WhateverButNotZero { get; set; }
Hopefully, What is the regex for “Any positive integer, excluding 0” will be helpful to find out the regular expression as per your need.
Related
I am using Microsoft.Extension.Options in ASP.NET Core 3.1 and I want to validate entries in an configuration file.
For this I want that, e.g. a RangeAttribute is applied to each element of an IEnumerable.
class MyConfiguration
{
[ApplyToItems]
[Range(1, 10)]
publlic IList<int> MyConfigValues { get; set; }
}
Or something like that. How do I write the ApplyToItems method?
As far as I know there is no way to retrieve the other ValidationAttributes while a possible ApplyToItems is validated.
Alternatively I could imagine something like:
[Apply(Range(1, 10)]
public List<int> MyConfigValues { get; set; }
but is that even valid syntax? How would I write an Attribute like Apply that takes other Attributes as parameter without falling back on something like
[Apply(new RangeAttribute(1, 10)]
which does not look nice.
To create a custom data annotation validator follow these gudelines:
Your class has to inherit from System.ComponentModel.DataAnnotations.ValidationAttribute class.
Override bool IsValid(object value) method and implement validation logic inside it.
That's it.
(from How to create Custom Data Annotation Validators)
So in your case it could be something like this:
public class ApplyRangeAttribute : ValidationAttribute
{
public int Minimum { get; set; }
public int Maximum { get; set; }
public ApplyRangeAttribute()
{
this.Minimum = 0;
this.Maximum = int.MaxValue;
}
public override bool IsValid(object value)
{
if (value is IList<int> list)
{
if (list.Any(i => i < Minimum || i > Maximum))
{
return false;
}
return true;
}
return false;
}
}
Edit
Here's how you would use it:
class MyConfiguration
{
[ApplyRange(Minimum = 1, Maximum = 10)]
public IList<int> MyConfigValues { get; set; }
}
I'm trying to build a series of attribute classes to make it easier for our development team to validate objects. The objects are POCO classes like this.
public class User
{
public string Name { get; set; }
public string Company { get; set; }
}
I want to decorate this model with a custom attribute.
public class User
{
[MustHaveValue]
public string Name { get; set; }
public string Company { get; set; }
}
Then I would create my own class implementing ValidationAttribute, the base class in .NET Framework, which belongs to System.ComponentModel.DataAnnotations.
public class MustHaveValueAttribute : ValidationAttribute
{
.
.
public override IsValid(object value)
{
// validation logic.
}
}
And then I can validate the User model whenever I want by making the set of instances like ValidationContext, List<ValidationResult>.
But in an enterprise environment, problems just can't be solved by a specific class. My validation scenario requires more complex and more flexible ways. Imagine that one of the required validation scenarios would something like this.
public class User
{
public string Name { get; set; }
public string Company { get; set; }
// Check if an item exists in this list.
[MustHaveMoreThanOneItem]
public IList<Client> Clients { get; set; }
}
Then I would need to make another attribute class
public class MustHaveMoreThanOneItemAttribute : ValidationAttribute
{
.
.
public override IsValid(object value)
{
// Let's assume this value is List<Client> for now.
// I know the exact type, so I'm going to cast it to List<Client> without further considerations
List<Client> clients = value as List<Client>;
if(clients.Count > 0) {
return true;
} else {
return false;
}
}
}
But the problem is that there are a lot of other models that have a nested list items. Try to imagine the time when I want to reuse the MustHaveMoreThanOneItem in one of the other models like...
public class Department
{
public string Name { get; set; }
[MustHaveMoreThanOneItem]
public IList<Employee> { get; set; }
}
You already know that it's not going to work because it was strongly typed only for List<Client>. So I decided to use Generic there to solve this problem.
But to my disappointment, the _Attribute interface doesn't support Generic. There's no additional implementation like _Attribute<T> : Attribute and therefore, no ValidationAttribute<T> alas!! I just cannot use Generic here !!
public class Department
{
public string Name { get; set; }
// No way to use this syntax.
[MustHaveMoreThanOneItem<Employee>]
public IList<Employee> { get; set; }
}
So I made a conclusion that Attribute must have been designed for a fixed set of validations like email format, card format, null check, and etc IMAO.
But I still want to use an attribute and give a lot of flexibilities in it to prevent the duplicated, verbose validation codes like this.
if(model.Clients.Count > 0) ...
if(model.Name != null) ...
if(model.Clients.GroupBy(x => x.Country == Country.USA).Count >= 1) ...
if(model.Clients.Where(x => x.CompanyName == Company.Google).ToList().Count > 1 ) ...
.
.
.
I want to pose two questions here.
If Attirbute supports Generic, this problem will be solved?
Is there any way to implement Generic Attribute? in order to use
[MustHaveMoreThanOneItem<Employee>] annotation on a class member?
You can generically check any object that implements IEnumerable like this:
public class MustHaveMoreThanOneItemAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
// omitted null checking
var enumerable = value as IEnumerable;
var enumerator = enumerable.GetEnumerator();
if (!enumerator.MoveNext())
{
return false;
}
if (!enumerator.MoveNext())
{
return false;
}
return true;
}
}
C# by definition does not support generic type attributes, although this has been requested actively for a long time:
https://github.com/dotnet/roslyn/issues/953
https://github.com/dotnet/csharplang/issues/124
However, you can still inject a type into a validation attribute via constructor. You then can use reflection or whatever you need to define your custom validation criteria.
public class MustHaveMoreThanOneItemAttribute : ValidationAttribute
{
public Type EnumerableType { get; }
public MustHaveMoreThanOneItemAttribute(Type t)
=> this.EnumerableType = typeof(ICollection<>).MakeGenericType(t);
public override bool IsValid(object value)
{
var count = this.EnumerableType.GetProperty("Count").GetValue(value) as int?;
return (count ?? 0) > 1;
}
}
Now this allows you to use something similar to your goal:
public class Department
{
public string Name { get; set; }
[MustHaveMoreThanOneItem(typeof(Employee))]
public IList<Employee> { get; set; }
}
How do you set different MaxLength attributes on a class consumed by many classes where the MaxLength can be different for each consuming class.
In my case, I am using Entity Framework and MVC. I have a complex type in my Entity Data Model for BilingualStrings which consist of an English and a French string. I have many entities that have a bilingual string which uses the complex type to map the English and French to the correct database table/column. So each table has two columns, but the entity has one property of type BilingualString. The field length is always the same for each English or French in a single table, but can be different for each table.
Here is a simplified example of what I am trying to achieve:
public partial class BilingualString
{
//[MaxLength(40)] Cannot put MaxLength here because it would apply to all instances of BilingualString
public string English { get; set; }
public string French { get; set; }
}
public class ClassWithShortDescription
{
//[MaxLength(20)] Cannot put MaxLength here because it does not makes sense. It needs to be on each English and French properties.
public BilingualString Description { get; set; }
}
public class ClassWithLongDescription
{
//[MaxLength(200)] Cannot put MaxLength here because it does not makes sense. It needs to be on each English and French properties.
public BilingualString Description { get; set; }
}
After some reading and lots of googling, I concluded that the underlying issue is that the meta data set by data attributes is static. Therefore the nested class properties cannot have different meta data even if consumed by different classes. The solution is to put the meta data on the property of the consuming class. Create a custom maxlength attribute that is applied to the property of type BilingualString.
Custom MaxLength Attribute for BilingualString
public class MaxLengthBilingualStringAttribute : MaxLengthAttribute
{
public MaxLengthBilingualStringAttribute(int length) : base(length)
{
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
BilingualString bilingualString = new BilingualString();
if (value.GetType() == typeof(EdmBilingualStringVarCharSingleLine))
{
var bs = value as EdmBilingualStringVarCharSingleLine;
bilingualString.English = bs.English;
bilingualString.French = bs.French;
}
else
return new ValidationResult("MaxLengthBilingualString Attribute does cannot be used with this type.");
if (bilingualString.English != null && bilingualString.English.Length > this.Length )
return new ValidationResult(string.Format("The maximum field length of {0} has been exceed for {1} English.", this.Length, validationContext.DisplayName));
if (bilingualString.French != null && bilingualString.French.Length > this.Length)
return new ValidationResult(string.Format("The maximum field length of {0} has been exceed for {1} French.", this.Length, validationContext.DisplayName));
return ValidationResult.Success;
}
}
Example Implementation:
public partial class BilingualString
{
public string English { get; set; }
public string French { get; set; }
}
public class ClassWithShortDescription
{
[MaxLengthBilingualString(20)]
public BilingualString Description { get; set; }
}
public class ClassWithLongDescription
{
[MaxLengthBilingualString(200)]
public BilingualString Description { get; set; }
}
As English and French are hard-coded in BilingualString, you can also hard-code two attributes instead of one:
public class ClassWithShortDescription
{
[MaxLengthForEnglish(20)]
[MaxLengthForFrench(25)]
public BilingualString Description { get; set; }
}
public class ClassWithLongDescription
{
[MaxLengthForEnglish(200)]
[MaxLengthForFrench(250)]
public BilingualString Description { get; set; }
}
Is there any way of using data annotations to compare two form field (eg. to confirm an email address) are the same, before allowing the form to be posted?
eg. can the regular expression data annotation use the match function to reference another property in a ViewModel?
Use the CompareAttribute
public string EmailAddress {get; set;}
[Compare(nameof(EmailAddress), ErrorMessage = "Emails mismatch")]
public string VerifiedEmailAddress { get; set; }
As one possibe option self-validation:
Implement an interface IValidatableObject with method Validate, where you can put your validation code.
public class TestModel : IValidatableObject
{
public string Email{ get; set; }
public string ConfirmEmail { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Email != ConfirmEmail)
{
yield return new ValidationResult("Emails mismatch", new [] { "ConfirmEmail" });
}
}
}
Please notice: this is only server-side validation.
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.
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; }
}
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.
ExpressiveAnnotations gives you such a possibility:
[Required]
[AssertThat("Length(FieldA) + Length(FieldB) + Length(FieldC) + Length(FieldD) > 50")]
public string FieldA { get; set; }
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.
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
Code Project - Custom Validation Attribute in ASP.NET MVC3
Haacked - ASP.NET MVC 2 Custom Validation
A bit late to answer, but for who is searching.
You can easily do this by using an extra property with the data annotation:
public string foo { get; set; }
public string bar { get; set; }
[MinLength(20, ErrorMessage = "too short")]
public string foobar
{
get
{
return foo + bar;
}
}
That's all that is too it really. If you really want to display in a specific place the validation error as well, you can add this in your view:
#Html.ValidationMessage("foobar", "your combined text is too short")
doing this in the view can come in handy if you want to do localization.
Hope this helps!