So what I like to do is:
[NotLike(Value = "Forbidden value")]
public string Title { get; set; }
Is it possible? I've read the docs from Microsoft and could not find anything like this.
You should be using ValidationAttribute and inherit from it as follows:
public class NotLikeAttribute : ValidationAttribute
{
private string _NotLikeStr = "";
public NotLikeAttribute(string notLikeStr)
{
this._NotLikeStr = notLikeStr;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
if (!((string)value).Contains(_NotLikeStr))
{
var memberName = validationContext.MemberName;
var errorMsg = "Your Message";
return new ValidationResult(errorMsg);
}
}
return null;
}
}
and decorate your property as follows:
[NotLike("Forbidden value")]
public string Title { get; set; }
of course instead of using line below
if (!((string)value).Contains(_NotLikeStr))
you can split string to multiple words or use Regular expression or anything that meets your requirements .
You can use regular expression for this
[RegularExpression(#"^((?!Forbidden value).)*$", ErrorMessage = "Characters are not allowed.")]
public string Title { get; set; }
I have two solution for you question:
1. Use [RegularExpression()]
You can use regular expression and create your own pattern for validation
For more information have a look at this link: Data annotation regular expression
2. Create new Custom Data annotation
You can create new custom data annotation (like what you did in question)
For more information have a look at this link: How to create Custom Data Annotation Validators
In MVC when we post a model to an action we do the following in order to validate the model against the data annotation of that model:
if (ModelState.IsValid)
If we mark a property as [Required], the ModelState.IsValid will validate that property if contains a value or not.
My question: How can I manually build and run custom validator?
P.S. I am talking about backend validator only.
In .NET Core, you can simply create a class that inherits from ValidationAttribute. You can see the full details in the ASP.NET Core MVC Docs.
Here's the example taken straight from the docs:
public class ClassicMovieAttribute : ValidationAttribute
{
private int _year;
public ClassicMovieAttribute(int Year)
{
_year = Year;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
Movie movie = (Movie)validationContext.ObjectInstance;
if (movie.Genre == Genre.Classic && movie.ReleaseDate.Year > _year)
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
}
I've adapted the example to exclude client-side validation, as requested in your question.
In order to use this new attribute (again, taken from the docs), you need to add it to the relevant field:
[ClassicMovie(1960)]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
Here's another, simpler example for ensuring that a value is true:
public class EnforceTrueAttribute : ValidationAttribute
{
public EnforceTrueAttribute()
: base("The {0} field must be true.") { }
public override bool IsValid(object value) =>
value is bool valueAsBool && valueAsBool;
}
This is applied in the same way:
[EnforceTrue]
public bool ThisShouldBeTrue { get; set; }
Edit: Front-End Code as requested:
<div asp-validation-summary="All" class="text-danger"></div>
The options are All, ModelOnly or None.
To create a custom validation attribute in .Net Core, you need to inherit from IModelValidator and implement Validate method.
Custom validator
public class ValidUrlAttribute : Attribute, IModelValidator
{
public string ErrorMessage { get; set; }
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
{
var url = context.Model as string;
if (url != null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
{
return Enumerable.Empty<ModelValidationResult>();
}
return new List<ModelValidationResult>
{
new ModelValidationResult(context.ModelMetadata.PropertyName, ErrorMessage)
};
}
}
The model
public class Product
{
public int ProductId { get; set; }
[Required]
public string ProductName { get; set; }
[Required]
[ValidUrl]
public string ProductThumbnailUrl { get; set; }
}
Will this approach give opportunity to work with "ModelState.IsValid" property in controller action method?
Yes! The ModelState object will correctly reflect the errors.
Can this approach be applied to the model class? Or it can be used with model class properties only?
I don't know if that could be applied onto class level. I know you can get the information about the class from ModelValidationContext though:
context.Model: returns the property value that is to be validated
context.Container: returns the object that contains the property
context.ActionContext: provides context data and describes the action method that processes the request
context.ModelMetadata: describes the model class that is being validated in detail
Notes:
This validation attribute doesn't work with Client Validation, as requested in OP.
I need to set RegularExpression Dynamically in Model.
I mean, I have stored RegularExpression in table and then I will store that value in one variable.
Now I want to supply that variable in Regular Express validation.
i.e
[RegularExpression(VariableValue, ErrorMessage = "Valid Phone is required")]
Something like
i.e
string CustomExpress = "#"^(\+|\d)(?:\+?1[-. ]?)?\(?([0-9]{2})\)?[-. ]?([0-9]{1})[-. ]?([0-9]{9})$" (from Database's table)
[RegularExpression(CustomExpress, ErrorMessage = "Valid Phone is required")]
public string Phone { get; set; }
You have two options, either you create your own validation attribute or you make your whole model "validatable".
Option 1
public class RegexFromDbValidatorAttribute : ValidationAttribute
{
private readonly IRepository _db;
//parameterless ctor that initializes _db
public override ValidationResult IsValid(object value, ValidationContext context)
{
string regexFromDb = _db.GetRegex();
Regex r = new Regex(regexFromDb);
if (value is string && r.IsMatch(value as string)){
return ValidationResult.Success;
}else{
return new ValidationResult(FormatMessage(context.DisplayName));
}
}
}
Then on your model:
[RegexFromDbValidator]
public string Telephone {get; set;}
Option 2
public SomeModel : IValidatableObject
{
private readonly IRepository _db;
//don't forget to initialize _db in ctor
public string Telephone {get; set;}
public IEnumerable<ValidationResult> Validate(ValidationContext context)
{
string regexFromDb = _db.GetRegex();
Regex r = new Regex(regexFromDb);
if (!r.IsMatch(Telephone))
yield return new ValidationResult("Invalid telephone number", new []{"Telephone"});
}
}
Here's a good resource that explains how to create validation attributes
Here's an example of using IValidatableObject
As Murali stated Data Annotation attributes values must be compile time constants.
If you want to perform dynamic validation based on other model values you can try with some kind of third party framework (e.g. Fluent Validation, it even can be integrated in ASP.NET's model validation too).
I believe there might be a way to implement this by inheriting the IValidatableObject interface. Upon doing so whenever the ModelState gets validated on the server side you could perform all the necessary checks that you wish.
It would look something like:
public class SomeClass: IValidatableObject {
private RegEx validation;
public SomeClass(RegEx val) {
this.validation = val;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
// perform validation logic - check regex etc...
// if an error occurs:
results.Add(new ValidationResult('error message'));
}
}
I am having problems with my custom validation in asp.net mvc 3.0
What I want to it to do.
Be set on a property (right now I only can figure out how to make it on the class)
Be smart enough to realize there are multiple instances being used.
Scenario
textbox ( id = "textbox1")
dropdownlist (id ="ddl1")
textbox (id = "textbox2")
4 dropdownlist (id = "ddl2")
the values in the dropdownlist are the same. ("Days" and "Minutes")
Now user types in textbox1 30 and chooses "days" in ddl1. He then types in textbox2 10000 and "days" in ddl2. First one is valid second one is invalid as there is only 365 days in a year.
Scenario 2
User types in texbox1 99 and chooses "minutes" in ddl1. This of course is invalid and should fail.
So there could be any combination of where they might choose a valid day and invalid minute time.
Or both could be valid
So I have this so far
My viewmodel
[ReminderFormat(ErrorMessage = "test")]
public class ReminderViewModel
{
public List<SelectListItem> DurationTypes { get; set; }
public DurationTypes SelectedDurationType { get; set; }
public string ReminderLength { get; set; }
}
This will be in a list of view models and generates the number of ones I need
List<ReminderViewModel> viewModel = new List<ReminderViewModel>()
// add ReminderviewModel to this collection
View
// do a for loop through the viewModel ( List<ReminderViewModel> )
// generate a textbox html box and drop down list for each one
Data annotation
// set attribute to class for now but I want it on a property but I can't
// figure out how to get SelectedDurationType and pass it in. if I have my attribute on ReminderLength
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ReminderFormatAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
var reminderViewModel = (ReminderViewModel)value;
switch (reminderViewModel.SelectedDurationType)
{
case DurationTypes.Days:
int length = Convert.ToInt32(reminderViewModel.ReminderLength);
if (length > 30)
{
return false;
}
else
{
return true;
}
default:
throw new ArgumentOutOfRangeException();
}
}
Problem 1
So as you can see I have the annotation on the class and I rather have it on the ReminderLength property.
Problem 2
Right now I just have days at 30 so it's less to type(I will change it later). The problem I am finding right now is that if textbox1 has 31 in it this will fail my validation.
This is correct. But if I have textbox2 that has 1 as the value it will pass validation. This is also correct.
What is not correct is that it will override the first invalid validation. So now it thinks all validation passed and goes into my action method. When it should reject it and go back to the view and tell the user that textbox1 failed validation.
Here's what I would do for your validation attribute:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class ReminderFormatAttribute: ValidationAttribute, IClientValidatable
{
public string DurationProperty { get; set; }
public ReminderFormatAttribute(string durationProperty)
{
DurationProperty = durationProperty;
ErrorMessage = "{0} value doesn't work for selected duration";
}
public override string FormatErrorMessage(string propName)
{
return string.Format(ErrorMessage, propName);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var durationType = validationContext.ObjectType.GetProperty(DurationProperty).GetValue(validationContext.ObjectInstance, null);
var reminderLength = value;
// Do your switch statement on durationType here
// Here is a sample of the correct return values
switch (durationType)
{
case DurationTypes.Days:
if (reminderLength > 30)
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
else
{
return null;
}
}
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "reminderformat",
};
rule.ValidationParameters["durationproperty"] = DurationProperty;
yield return rule;
}
}
Now in your ViewModel you can annotate the ReminderLength property like so:
[ReminderFormat("SelectedDurationType")]
public string ReminderLength { get; set; }
This is how I usually do it when the validation depends on the value of another property. The GetClientValidationRules method is the server side piece you need to tie into the unobtrusive client validation via jquery validate.
Check out this link for another ValidationAttribute example: http://www.devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-2
The example on this site also goes into writing the necessary jQuery to tie in the client validation
How to use data annotations to do a conditional validation on model?
For example, lets say we have the following model (Person and Senior):
public class Person
{
[Required(ErrorMessage = "*")]
public string Name
{
get;
set;
}
public bool IsSenior
{
get;
set;
}
public Senior Senior
{
get;
set;
}
}
public class Senior
{
[Required(ErrorMessage = "*")]//this should be conditional validation, based on the "IsSenior" value
public string Description
{
get;
set;
}
}
And the following view:
<%= Html.EditorFor(m => m.Name)%>
<%= Html.ValidationMessageFor(m => m.Name)%>
<%= Html.CheckBoxFor(m => m.IsSenior)%>
<%= Html.ValidationMessageFor(m => m.IsSenior)%>
<%= Html.CheckBoxFor(m => m.Senior.Description)%>
<%= Html.ValidationMessageFor(m => m.Senior.Description)%>
I would like to be the "Senior.Description" property conditional required field based on the selection of the "IsSenior" propery (true -> required). How to implement conditional validation in ASP.NET MVC 2 with data annotations?
There's a much better way to add conditional validation rules in MVC3; have your model inherit IValidatableObject and implement the Validate method:
public class Person : IValidatableObject
{
public string Name { get; set; }
public bool IsSenior { get; set; }
public Senior Senior { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (IsSenior && string.IsNullOrEmpty(Senior.Description))
yield return new ValidationResult("Description must be supplied.");
}
}
Read more at Introducing ASP.NET MVC 3 (Preview 1).
I have solved this by handling the "ModelState" dictionary, which is contained by the controller. The ModelState dictionary includes all the members that have to be validated.
Here is the solution:
If you need to implement a conditional validation based on some field (e.g. if A=true, then B is required), while maintaining property level error messaging (this is not true for the custom validators that are on object level) you can achieve this by handling "ModelState", by simply removing unwanted validations from it.
...In some class...
public bool PropertyThatRequiredAnotherFieldToBeFilled
{
get;
set;
}
[Required(ErrorMessage = "*")]
public string DepentedProperty
{
get;
set;
}
...class continues...
...In some controller action ...
if (!PropertyThatRequiredAnotherFieldToBeFilled)
{
this.ModelState.Remove("DepentedProperty");
}
...
With this we achieve conditional validation, while leaving everything else the same.
UPDATE:
This is my final implementation: I have used an interface on the model and the action attribute that validates the model which implements the said interface. Interface prescribes the Validate(ModelStateDictionary modelState) method. The attribute on action just calls the Validate(modelState) on IValidatorSomething.
I did not want to complicate this answer, so I did not mention the final implementation details (which, at the end, matter in production code).
I had the same problem yesterday but I did it in a very clean way which works for both client side and server side validation.
Condition: Based on the value of other property in the model, you want to make another property required. Here is the code
public class RequiredIfAttribute : RequiredAttribute
{
private String PropertyName { get; set; }
private Object DesiredValue { get; set; }
public RequiredIfAttribute(String propertyName, Object desiredvalue)
{
PropertyName = propertyName;
DesiredValue = desiredvalue;
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
Object instance = context.ObjectInstance;
Type type = instance.GetType();
Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
if (proprtyvalue.ToString() == DesiredValue.ToString())
{
ValidationResult result = base.IsValid(value, context);
return result;
}
return ValidationResult.Success;
}
}
Here PropertyName is the property on which you want to make your condition
DesiredValue is the particular value of the PropertyName (property) for which your other property has to be validated for required
Say you have the following
public class User
{
public UserType UserType { get; set; }
[RequiredIf("UserType", UserType.Admin, ErrorMessageResourceName = "PasswordRequired", ErrorMessageResourceType = typeof(ResourceString))]
public string Password
{
get;
set;
}
}
At last but not the least , register adapter for your attribute so that it can do client side validation (I put it in global.asax, Application_Start)
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),typeof(RequiredAttributeAdapter));
I've been using this amazing nuget that does dynamic annotations ExpressiveAnnotations
You could validate any logic you can dream of:
public string Email { get; set; }
public string Phone { get; set; }
[RequiredIf("Email != null")]
[RequiredIf("Phone != null")]
[AssertThat("AgreeToContact == true")]
public bool? AgreeToContact { get; set; }
You can disable validators conditionally by removing errors from ModelState:
ModelState["DependentProperty"].Errors.Clear();
Thanks Merritt :)
I've just updated this to MVC 3 in case anyone finds it useful: Conditional Validation in ASP.NET MVC 3.
There is now a framework that does this conditional validation (among other handy data annotation validations) out of the box:
http://foolproof.codeplex.com/
Specifically, take a look at the [RequiredIfTrue("IsSenior")] validator. You put that directly on the property you want to validate, so you get the desired behavior of the validation error being associated to the "Senior" property.
It is available as a NuGet package.
You need to validate at Person level, not on Senior level, or Senior must have a reference to its parent Person. It seems to me that you need a self validation mechanism that defines the validation on the Person and not on one of its properties. I'm not sure, but I don't think DataAnnotations supports this out of the box. What you can do create your own Attribute that derives from ValidationAttribute that can be decorated on class level and next create a custom validator that also allows those class-level validators to run.
I know Validation Application Block supports self-validation out-of the box, but VAB has a pretty steep learning curve. Nevertheless, here's an example using VAB:
[HasSelfValidation]
public class Person
{
public string Name { get; set; }
public bool IsSenior { get; set; }
public Senior Senior { get; set; }
[SelfValidation]
public void ValidateRange(ValidationResults results)
{
if (this.IsSenior && this.Senior != null &&
string.IsNullOrEmpty(this.Senior.Description))
{
results.AddResult(new ValidationResult(
"A senior description is required",
this, "", "", null));
}
}
}
I had the same problem, needed a modification of [Required] attribute - make field required in dependence of http request.The solution was similar to Dan Hunex answer, but his solution didn't work correctly (see comments). I don't use unobtrusive validation, just MicrosoftMvcValidation.js out of the box.
Here it is. Implement your custom attribute:
public class RequiredIfAttribute : RequiredAttribute
{
public RequiredIfAttribute(/*You can put here pararmeters if You need, as seen in other answers of this topic*/)
{
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
//You can put your logic here
return ValidationResult.Success;//I don't need its server-side so it always valid on server but you can do what you need
}
}
Then you need to implement your custom provider to use it as an adapter in your global.asax
public class RequreIfValidator : DataAnnotationsModelValidator <RequiredIfAttribute>
{
ControllerContext ccontext;
public RequreIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
: base(metadata, context, attribute)
{
ccontext = context;// I need only http request
}
//override it for custom client-side validation
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
//here you can customize it as you want
ModelClientValidationRule rule = new ModelClientValidationRule()
{
ErrorMessage = ErrorMessage,
//and here is what i need on client side - if you want to make field required on client side just make ValidationType "required"
ValidationType =(ccontext.HttpContext.Request["extOperation"] == "2") ? "required" : "none";
};
return new ModelClientValidationRule[] { rule };
}
}
And modify your global.asax with a line
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequreIfValidator));
and here it is
[RequiredIf]
public string NomenclatureId { get; set; }
The main advantage for me is that I don't have to code custom client validator as in case of unobtrusive validation. it works just as [Required], but only in cases that you want.
Check out Simon Ince's Conditional Validation in MVC.
I am working through his example project right now.
Typical usage for conditional removal of error from Model State:
Make conditional first part of controller action
Perform logic to remove error from ModelState
Do the rest of the existing logic (typically Model State validation, then everything else)
Example:
public ActionResult MyAction(MyViewModel vm)
{
// perform conditional test
// if true, then remove from ModelState (e.g. ModelState.Remove("MyKey")
// Do typical model state validation, inside following if:
// if (!ModelState.IsValid)
// Do rest of logic (e.g. fetching, saving
In your example, keep everything as is and add the logic suggested to your Controller's Action. I'm assuming your ViewModel passed to the controller action has the Person and Senior Person objects with data populated in them from the UI.
I'm using MVC 5 but you could try something like this:
public DateTime JobStart { get; set; }
[AssertThat("StartDate >= JobStart", ErrorMessage = "Time Manager may not begin before job start date")]
[DisplayName("Start Date")]
[Required]
public DateTime? StartDate { get; set; }
In your case you would say something like "IsSenior == true".
Then you just need to check the validation on your post action.