I do have a View bound to a model,
When I am using a built-in Validator on a property in the model, [EmailAddress] for instance, if I write something invalid, the html tag gets the class input-validation-error. Which allows me to display it in red via css. That's works perfectly.
But when I am using my custom validators, like :
public class CONT_RU06Attribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
int value = int.Parse(value.ToString());
if (value < 2 || value > 6)
return new ValidationResult("Value must be between 2 and 6");
}
return ValidationResult.Success;
}
}
The bound html tag does not get the input-validation-error class...
What can I do ?
You need to implement IClientValidatable also
public class CONT_RU06Attribute : ValidationAttribute, IClientValidatable
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
int value = int.Parse(value.ToString());
if (value < 2 || value > 6)
return new ValidationResult("Value must be between 2 and 6");
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule();
rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
rule.ValidationType = "range";
yield return rule;
}
}
Check this article
Related
Want to apply validation on input model using series of custom validation attributes as mentioned below.
If validation result of first validation attribute ie "ValidatorAttributeOne" is true than no need to process "ValidatorAttributeTwo" validation logic.
To achieve that valid result of "ValidationAttributeOne" assigned to "validationContext.Items" dictionary believing that "validationContext" will share across the different "ValidationAttributes" in same http request but below line always throws below exception
var isDependedFilterValidated = (bool?)validationContext.Items[dependedFilter]
"message": "The given key 'ValidationAttributeOne' was not present in the dictionary.",
public class ValidatorAttributeOne : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
//custom validation
validationContext.Items["ValidatorAttributeOne"] = true;
return ValidationResult.Success;
}
}
public class ValidatorAttributeTwo : ValidationAttribute
{
private readonly string dependedFilter = default(string);
public UsernamesEmailValidatorAttribute()
{
}
public UsernamesEmailValidatorAttribute(string filter)
{
dependedFilter = filter;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var isDependedFilterValidated = (bool?)validationContext.Items[dependedFilter];
if (isDependedFilterValidated == false)
{
//custom validation logic
}
return ValidationResult.Success;
}
}
public class CustomeModel
{
[ValidatorAttributeOne ]
[ValidatorAttributeTwo("ValidatorAttributeOne")]
public string usernames { get; set; }
}
Firstly,two custom validation attribute cannot share validationContext,if you don't want to do validationContext when the first one is true.You can set the contents of IsValid method of two validationAttributes together.
For example:
public class ValidationAttributeThree : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (ValidationAttributeOne IsValid method Content is true) {
return ValidationResult.Success;
}
else if(ValidationAttributeTwo IsValid method Content is true){
return new ValidationResult("ValidationAttributeOne error message");
}else{
return new ValidationResult("ValidationAttributeOne error message"+"ValidationAttributeTwo error message");
}
}
}
I need to compare two properties in a class using .net data annotations. One of the two properties should be filled and the other should be blank. How can I override the behavior of the CompareAttribute ? If it is not possible, what's the alternative solution ?
This class works with one issue:
If Property A is set to something and property B already has a value, then property A becomes invalid as expected. Upon Blanking property B, property A should become valid but it won't until I try to modify property A so I trigger the validation again. Is there a way to connect the two together to trigger the validation on both one either one changes ?
class CustomAttribute : ValidationAttribute
{
private readonly string _other;
public CustomAttribute(string other)
{
_other = other;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(_other);
if (property == null)
{
return new ValidationResult(
string.Format("Unknown property: {0}", _other)
);
}
var otherValue = property.GetValue(validationContext.ObjectInstance, null);
if (!String.IsNullOrEmpty(value.ToString()) && !String.IsNullOrEmpty(otherValue.ToString()))
{
return new ValidationResult("Test");
}
return null;
}
}
For stuff like this I use ExpressiveAnnotations. It has a brilliant RequiredIf attribute:
[RequiredIf("B == null", ErrorMessage = "Either A or B should be filled")]
public string A { get; set; }
[RequiredIf("A == null", ErrorMessage = "Either A or B should be filled")]
public string B { get; set; }
You can extend the CompareAttribute with your own class:
public class CustomCompareAttribute: CompareAttribute {
public CustomCompareAttribute(string otherProperty) : base(otherProperty) {
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
if (OtherProperty == null && value == null) {
return new ValidationResult("Either A or B should be filled");
}
// more checks here ...
}
}
I am trying to create my own validation attribute IsUnique that checks existing values for given property. I understand IsValid() must be overridden so that custom validation attribute can work. So far I have seen examples with validate attributes that take a string parameters which is then compared with hard coded values inside IsValid() method.
I need IsValid() method to get access to a property and its value to further compare it with values in the database.
This is what I have done so far:
public class IsUnique : ValidationAttribute
{
private string codeno { get; set; }
: base("{0} is already in use")
public IsUnique (string codeno)
{
this.codeno = codeno;
}
public override ValidationResult IsValid(object value,
ValidationContext vContext)
{
if (value != null)
{
MyDBContext db = new MyDBContext();
Student studentsCodeNo =
db.Students.FirstOrDefault(r => r.codeno== (string)value);
if (studentsCodeNo != null)
{
string errorMessage =
FormatErrorMessage(vContext.DisplayName);
return new ValidationResult(errorMessage);
}
}
return ValidationResult.Success;
}
}
As said, the problem is that this version takes parameter. I would like codeno to be read from a user form field, and such value would then be compared against anything in database. I don't know how to read values from the form fields.
Here is code
public class IsUnique : ValidationAttribute{
public override ValidationResult IsValid(object value,
ValidationContext vContext)
{
PropertyInfo property = validationContext.ObjectType.GetProperty("Codeno");
if (property == null)
return new ValidationResult(string.Format("Property '{0}' is undefined","Codeno"));
var fieldValue = property.GetValue(validationContext.ObjectInstance, null);
string codeno= (fieldValue == null ? "" : fieldValue.ToString());
if (!string.IsNullOrEmpty(codeno))
{
MyDBContext db = new MyDBContext();
Student studentsCodeNo =
db.Students.FirstOrDefault(r => r.codeno== codeno);
if (studentsCodeNo != null)
{
string errorMessage =
FormatErrorMessage(vContext.DisplayName);
return new ValidationResult(errorMessage);
}
}
return ValidationResult.Success; }}
There is sort of an out of the box way to do this already
http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.schema.indexattribute(v=vs.113).aspx
[Index(IsUnique=true)]
I made custom Validator attribute
partial class DataTypeInt : ValidationAttribute
{
public DataTypeInt(string resourceName)
{
base.ErrorMessageResourceType = typeof(blueddPES.Resources.PES.Resource);
base.ErrorMessageResourceName = resourceName;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
string number = value.ToString().Trim();
int val;
bool result = int.TryParse(number,out val );
if (result)
{
return ValidationResult.Success;
}
else
{
return new ValidationResult("");
}
}
}
But when entered string instead of int value in my textbox then value==null and when i entered int value then value==entered value;. Why?
Is there any alternate by which i can achieve the same (make sure at server side only)
The reason this happens is because the model binder (which runs before any validators) is unable to bind an invalid value to integer. That's why inside your validator you don't get any value. If you want to be able to validate this you could write a custom model binder for the integer type.
Here's how such model binder could look like:
public class IntegerBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
int temp;
if (value == null ||
string.IsNullOrEmpty(value.AttemptedValue) ||
!int.TryParse(value.AttemptedValue, out temp)
)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, "invalid integer");
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);
return null;
}
return temp;
}
}
and you will register it in Application_Start:
ModelBinders.Binders.Add(typeof(int), new IntegerBinder());
But you might ask: what if I wanted to customize the error message? After all, that's what I was trying to achieve in the first place. What's the point of writing this model binder when the default one already does that for me, it's just that I am unable to customize the error message?
Well, that's pretty easy. You could create a custom attribute which will be used to decorate your view model with and which will contain the error message and inside the model binder you will be able to fetch this error message and use it instead.
So, you could have a dummy validator attribute:
public class MustBeAValidInteger : ValidationAttribute, IMetadataAware
{
public override bool IsValid(object value)
{
return true;
}
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.AdditionalValues["errorMessage"] = ErrorMessage;
}
}
that you could use to decorate your view model:
[MustBeAValidInteger(ErrorMessage = "The value {0} is not a valid quantity")]
public int Quantity { get; set; }
and adapt the model binder:
public class IntegerBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
int temp;
var attemptedValue = value != null ? value.AttemptedValue : string.Empty;
if (!int.TryParse(attemptedValue, out temp)
)
{
var errorMessage = "{0} is an invalid integer";
if (bindingContext.ModelMetadata.AdditionalValues.ContainsKey("errorMessage"))
{
errorMessage = bindingContext.ModelMetadata.AdditionalValues["errorMessage"] as string;
}
errorMessage = string.Format(errorMessage, attemptedValue);
bindingContext.ModelState.AddModelError(bindingContext.ModelName, errorMessage);
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);
return null;
}
return temp;
}
}
I have custom validation attribute such as this:
public class MyCustomAttribute : ValidationAttribute {
protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
if ((int)value == 100) {
// do some checking to validate & return ValidationResult accordingly
} else return ValidationResult.Success;
}
}
In usage like this:
[DisplayName("My Custom Property")]
[MyCustom(ErrorMessage = "ERROR!!!")]
public int? MyCustomProperty { get; set; }
My question is: why is it that inside MyCustomAttribute, within the IsValid method, validationContext is always NULL? Is there anything special I need to set to get it not to be NULL?
if you use
ValidationResult IsValid(object value, ValidationContext validationContext)
to check if data is valid you have to use
v.GetValidationResult(propertyValue,new ValidationContext(this))!= ValidationResult.Success
instead of
v.IsValid(propertyValue)
you must override RequiresValidationContext
public override bool RequiresValidationContext => true;
it will work