MVC Data Annotation And Custom Validation Attribute - c#

I have a field in a model on which builtin data annotation and custom validation attribute not seems to be working correctly. Looks like there is an issue with the order of there execution.
[Required]
[Display(Name = "UserName")]
[RegularExpression(#"^[^\s\,]*$", ErrorMessage = "Username Cannot must not contain any spaces or commas")]
[UnqiueUserName]
public string UserName { get; set; }
public class UnqiueUserName : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null && Membership.GetUser(value.ToString()) != null)
{
return new ValidationResult
(string.Format("UserName Already Exist"));
}
return ValidationResult.Success;
}
}
If I enter username user,123 The custom validation attribute executing first and that make the exception to occur.
The parameter "username" must not contain a comma
But If I enter user 123 the builtin Data Annotation Regular Expression execute first this time.
I want my Custom Validation attribute after the builtin one. Please suggest

Related

ASP.NET Core Web API - Custom Unique Email Validation using Data Annotation not working

I have Custom Unique Email validation using ASP.NET Core-6 Web API Entity Framework project
public class UserUniqueEmailValidator : ValidationAttribute
{
private readonly ApplicationDbContext _dbContext;
public UserUniqueEmailValidator(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public bool IsUniqueUserEmailValidator(string email)
{
if (_dbContext.ApplicationUsers.SingleOrDefault(x => x.Email.ToLower() == email.ToLower()) == null) return true;
return false;
}
}
Then I called it here:
[Required(ErrorMessage = "Email field is required. ERROR!")]
[StringLength(100, ErrorMessage = "Email field must not exceed 100 characters.")]
[EmailAddress(ErrorMessage = "The Email field is not a valid e-mail address.")]
[UserUniqueEmailValidator(ErrorMessage = "Email already exists !!!")]
public string Email { get; set; }
But I got this error:
There is no argument given that corresponds to the required formal parameter 'dbContext' of 'UserUniqueEmailValidator.UserUniqueEmailValidator(ApplicationDbContext)'
How do I resolve it?
Thanks
Instead of implementing your own validation attribute, use the Remote Validation attribute.
Here is an example implementation
[Remote("ValidateEmailAddress","Home")]
public string Email { get; set; }
The validateEmailAddress implementation
public IActionResult ValidateEmailAddress(string email)
{
return Json(_dbContext.ApplicationUsers.Any(x => x.Email != email) ?
"true" : string.Format("an account for address {0} already exists.", email));
}
Hope it helps.
Just as the error indicates: Validator's constructor contains the ApplicationDbContext parameter which is not valid;
Also, IsUniqueUserEmailValidatormethod has not been called ,so the codes inside it will never be executed
If you want to custom ValidationAttribute you could overrride protected virtual ValidationResult? IsValid(object? value, ValidationContext validationContext)
and access ApplicationDbContext as below:
protected override ValidationResult? IsValid(
object? value, ValidationContext validationContext)
{
var context = validationContext.GetService<ApplicationDbContext>();
//add your logical codes here
return ValidationResult.Success;
}
For more details,you could check this document

Custom Validation Annotation for Checking if Username Already Exists

I've looked at a number of questions on creating custom data annotation classe for model validation and so far I can't extrapolate an answer from any of them as they all vary quite wildly in responses.
I am trying to run a method that returns a boolean on what's been entered into the textbox for that property but I'm not sure how to get at the property contents to run the method. Basically this is a check to see if this username already exists. Here is what I've tried but as you can see, I don't know how to get at the string entered into the field to run the method on.
public class Username : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ADSI adsi = new ADSI();
if (adsi.UserExists(//here's where the text entered should go))
{
return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}
I figured it out. The value object is the text that the user has entered into the field.
public class Username : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ADSI adsi = new ADSI();
if (adsi.UserExists(value.ToString()))
{
return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}
Obviously the method I use for checking if a username exists is something I wrote so you'll need to refer to documentation on how to do that.
Usage:
[Username(ErrorMessage = "Username already exists.")]
public string Username {get; set;}

Validations in ASP.NET MVC 4 - Validate sum of properties of objects on page

I'm new to MVC and am working through some validations. I've done my basic field validations at the model level (required, ranges, etc). I'm now working on a page which is essentially building a scorecard with weights. I need each of the weights for each criteria to add up to 1.
I'm not sure if I can validate this in the model because I need the ability to create each of these objects in the database as they're added. How would one go about validating that each of these properties add up to 1 before the user moves on to the next step?
Basically for the backend you need to create a custom validation that can be used as a attribute over the field to validate.
This is an example of a validation that checks if "number is greater than another filed".
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class NumberGreaterThanAttribute : ValidationAttribute, IClientValidatable
{
string otherPropertyName;
public NumberGreaterThanAttribute(string otherPropertyName, string errorMessage)
: base(errorMessage)
{
this.otherPropertyName = otherPropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ValidationResult validationResult = ValidationResult.Success;
if (value == null)
return validationResult;
try
{
// Using reflection we can get a reference to the other date property, in this example the project start date
var otherPropertyInfo = validationContext.ObjectType.GetProperty(this.otherPropertyName);
object referenceProperty = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
if (referenceProperty == null)
return validationResult;
double referenceProperty_value = System.Convert.ToDouble(referenceProperty, null);
double currentField_value = System.Convert.ToDouble(value, null);
if (currentField_value <= referenceProperty_value)
{
validationResult = new ValidationResult(ErrorMessageString);
}
}
catch (Exception ex)
{
throw ex;
}
return validationResult;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
//string errorMessage = this.FormatErrorMessage(metadata.DisplayName);
string errorMessage = ErrorMessageString;
// The value we set here are needed by the jQuery adapter
ModelClientValidationRule numberGreaterThanRule = new ModelClientValidationRule();
numberGreaterThanRule.ErrorMessage = errorMessage;
numberGreaterThanRule.ValidationType = "numbergreaterthan"; // This is the name the jQuery adapter will use
//"otherpropertyname" is the name of the jQuery parameter for the adapter, must be LOWERCASE!
numberGreaterThanRule.ValidationParameters.Add("otherpropertyname", otherPropertyName);
yield return numberGreaterThanRule;
}
}
REMEMBER: frontend validation is not automatically enabled, for custom/newly created validations. If you want to create the same validation rules for the frontend you'll need to create a new frontend custom validation. I generally use validate.js library for the frontend. Still, frontend validation is not strictly required (but I'd suggest to use it in any case) if you don't mind pushing data back and forth from server to client, especially if data is small.
The ViewModel/Model will be received and parsed by modelstate when the backend receives the request(generally a POST). The model/viewmodel should be decorated on the fields that require the validation as so:
public Int64? AtomicQtyMultiplePurchaseMin { get; set; }
[NumberGreaterThanAttribute("AtomicQtyMultiplePurchaseMin", "Must be greater than min num. qty. of purchase")]
public Int64? AtomicQtyMultiplePurchaseMax { get; set; }
I generally place the custom validation classes in the following folder (but you can place it where you want in the project):

Custom validation failing even when data filled in

I have created a custom validation rule that requires two textboxes if and only if the previous set of radio buttons shows Yes selected. Validation works fine if no radio selection or No is made, but fails when Yes is selected, if when data is supplied.
My custom validation is:
public class DamageSupport : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var incidentdetail = (IncidentDetail) validationContext.ObjectInstance;
return (incidentdetail.PropertyDamage == "Y") ? new ValidationResult("This field is required since Property Damage is set to 'Yes'") : ValidationResult.Success;
}
}
Here are my modelvalidation rules:
[MaxLength(1)]
[Display(Name = "Property Damaged?")]
public string PropertyDamage { get; set; }
[RegularExpression(#"^\d+.\d{0,2}$")]
[Range(0, 99999999.99)]
[Display(Name = "Estimated value of damage")]
[DamageSupport]
public decimal? DamageAmount { get; set; }
[Display(Name = "Description of the damage")]
[DamageSupport]
public string DamageDescription { get; set; }
I look at the data in the ModelState object data in debug mode, and all the data I types is there,just fails validation for some reason.
I double checked it wasn't a data type issue by removing the custom validation and saving the same data, works fine.
Anyone think of someplace for me to look?

How do I get the Validation message to disappear after adding a model error MVC?

In my view I have a check box and a text box if the check box is checked then I require the text box to be filled with some text. To do this I call
ModelState.AddModelError("item", "Please enter some text.");
only if the checkbox returns true and the text box isempty
when my page re-displays I receive the proper message where I have
#Html.ValidationMessageFor(model => model.item)
but I would like the text to go away after a use types something in the text box, without the user having to hit submit like it does with data annotation. How can I fix this?
I'm using c# Asp.net 4 with entity framework 5
ModelState.AddModelError is server-side validation, so the error message will not go away until you post to the server.
If you want the functionality you describe, you can define a custom validation attribute and apply it both client side and server-side. For example, you can define a "RequiredIf" custom validation attribute, which would make a field required if a certain other condition is met (in this case, if another property is true):
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;
}
}
Register it in your global.asax:
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),typeof(RequiredAttributeAdapter);
Then you can use it like this:
public class YourModel {
// This is the property tied to your checkbox
public bool YourBooleanProperty { get; set; }
[RequiredIf("YourBooleanProperty", true)]
public string Item { get; set; }
}
You could also leverage the JQuery Validate plugin to perform the same conditional validation client-side.
Try with jquery, attach an eventListener to the field and remove the class CSS that MVC added to the field and hide the validation label

Categories