Order of validation messages from custom attribute in MVC3 view model - c#

We have created the following view model in one of our MVC3 projects:
[PropertiesMustMatch("Email", "ConfirmEmail", ErrorMessage = "The email address you provided does not match the email address in the confirm email box.")]
[PropertiesMustMatch("NewPassword", "ConfirmPassword", ErrorMessage = "The new password you provided does not match the confirmation password in the confirm password box.")]
public class ActivationStep2ViewModel
{
.....
The PropertiesMustMatch is a custom attribute that we have created, code as below:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class PropertiesMustMatchAttribute : ValidationAttribute
{
private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";
private readonly object _typeId = new object();
public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)
: base(_defaultErrorMessage)
{
OriginalProperty = originalProperty;
ConfirmProperty = confirmProperty;
}
public string ConfirmProperty { get; private set; }
public string OriginalProperty { get; private set; }
public override object TypeId
{
get
{
return _typeId;
}
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
OriginalProperty, ConfirmProperty);
}
public override bool IsValid(object value)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value);
return Object.Equals(originalValue, confirmValue);
}
}
However, on the view, when there is a mismatch between both the 1) Email and Confirm email and 2) the password and confirm password, the validation message for the password is displayed on top. See image below:
We would like the validation message for the Email text boxes to be displayed on top as these text boxes appear before the password text boxes.
NOTE: The order of the messages on local build (through VS2010) works as expected. The order of messages is screwed up only in our DEV and TEST environments. Looking at the deployed DLLs through reflector, this is what is displayed: (the order of attributes is reversed)
What can we do to fix this on the release builds?
Any help / suggestions would be much appreciated.
Thanks.

We still do not know why the compiler muddles up the order of validation attributes set up, a bit crap!
We had to use a different approach to fix this.
We got rid of the custom validation attributes and implemented IValidatableObject on the view model. In the Validate method, added validation logic in the order that we wanted the messages displayed (code below):
public class ActivationStep2ViewModel : IValidatableObject
{
.
.
.
.
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if(Email != ConfirmEmail)
{
yield return new ValidationResult("The email address you provided does not match the email address in the confirm email box.");
}
if(NewPassword != ConfirmPassword)
{
yield return new ValidationResult("The new password you provided does not match the confirmation password in the confirm password box.");
}
}

Related

NET MAUI CommunityToolkit.MVVM throws error on email validation attribute

I have wrote an email validation attribute.
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class EmailAttribute : RegularExpressionAttribute
{
public EmailAttribute() : base(#"[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}")
{
this.ErrorMessage = "Please provide a valid email address";
}
}
When the validation has been called, the validator runs ok, and when exits throws an error:
System.Reflection.TargetInvocationException: 'Exception has been thrown by the target of an invocation.'
I tried changing the validator implementation, and for every it throws me the same error message.
If I remove the email validation attribute from the object's property everything works. If I add some new random validation attribute it works.
Here is my model:
public partial class LoginModel : BaseViewModel
{
private string email = string.Empty;
private string password = string.Empty;
[Required]
[MinLength(5)]
[Email]
public string Email
{
get => this.email;
set
{
SetProperty(ref this.email, value, true);
OnPropertyChanged("ErrorDictionary[Email]");
}
}
[Required]
[DataType(DataType.Password)]
[MinLength(8)]
public string Password
{
get => this.password;
set
{
SetProperty(ref this.password, value, true);
OnPropertyChanged("ErrorDictionary[Password]");
}
}
}
thnx
I get the same error failure when the ValidationResult is not a success and I return ValidationResult error message of type string.
If the validationResult is ValidationResult.Success then it works and even changes the control to a different color if I wish. Therefore it is the xaml view receiving an incompatible value in the binding expecting bool but gets a string

Localize DataAnnotations in Asp.net core

In my asp.net core (.net5) application I have a form with a required field. When the field is empty I have
"The XX field is required"... In English... I want to translate it in French. I mean, I don't really want to translate, I want to use the French version of the message. I don't want to add Resource files, because I have any custom strings to translate, I just want to use existing messages, but in French.
I started to read here but did't really get the point if the article really proposes me to translate each message manually by myself.
I added this one in the Configure
var supportedCultures = new[] { "fr-FR" };
var localizationOptions = new RequestLocalizationOptions().SetDefaultCulture(supportedCultures[0])
.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures);
app.UseRequestLocalization(localizationOptions);
however this didn't change the message...
nor setting the culture params via URL, like this
If you just want one french version, you just need to define the error message in [Required]
attribute, like:
public class MyModel
{
[Required(ErrorMessage = "Le champ Nom est obligatoire")]
public string Nom { get; set; }
}
And if your shared code refers to the documention you shared, you need to add Resource file to
define the translated message, or the message will not changed.
Edit:
If you have other properties, you can define a customRequired attribute:
public class CustomRequiredAttribute : ValidationAttribute
{
public CustomRequiredAttribute()
{
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
{
ErrorMessage = $"Le champ {validationContext.DisplayName} est obligatoire";
return new ValidationResult(ErrorMessage);
}
return ValidationResult.Success;
}
}
Model:
public class MyModel
{
[CustomRequired] //custom attribute
public string Nom { get; set; }
[CustomRequired]
public string PreNom { get; set; }
}
To use your custom attribute, you should add ModelState.IsValid in your post action, so if invalid, it will return the input view to show the errorMessages,like:
[HttpPost]
public IActionResult Privacy(MyModel myModel)
{
if (!ModelState.IsValid)
return View("Index");
return View();
}

ASP.NET MVC. How disable required validation based on a parameter?

I do have a entity class with some required attributes depending of a selector.
For instance: The Selector can assume "1" or "2". If selector was "1", a group of parameters shall be required. If selector is "2" another set of parameters is required.
class MyClass{
public int Selector {get;set;} // 1 or 2
public string A_required_for_1 {get;set;}
public string B_required_for_1 {get;set;}
public string C_required_for_2 {get;set;}
public string D_required_for_2 {get;set;}
public string E_Required_for_both_selectors {get;set;}
}
User should be able to switch between selectors during Create or Edit actions in view.
Client validation is already solved.
How can I deal with it in server validation?
You can either create your own custom validation attribute or use MVC Foolproof Validation and then do:
class MyClass
{
public int Selector {get;set;} // 1 or 2
[RequiredIf("Selector == 1", ErrorMessage = "Your Error Message")]
public string A_required_for_1 {get;set;}
[RequiredIf("Selector == 1", ErrorMessage = "Your Error Message")]
public string B_required_for_1 {get;set;}
[RequiredIf("Selector == 2", ErrorMessage = "Your Error Message")]
public string C_required_for_2 {get;set;}
[RequiredIf("Selector == 2", ErrorMessage = "Your Error Message")]
public string D_required_for_2 {get;set;}
[Required("Your Error Message")]
public string E_Required_for_both_selectors {get;set;}
}
As mentioned by Win it does not seem to have been in active development for a while so you may want to go down the route of creating your own custom validation attribute, which does require more work but you can have a finer control over the validation itself. Choose depending on your needs.
For a custom validation attribute you could do something like this:
public class RequiredIfOtherProperty : ValidationAttribute
{
private readonly string _otherPropertyName;
private readonly string _compareValue;
public RequiredIfOtherProperty(string otherPropertyName, string compareValue)
{
_otherPropertyName = otherPropertyName;
_compareValue = compareValue;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var otherProperty = validationContext.ObjectType.GetProperty(_otherPropertyName);
if (otherProperty == null)
{
return new ValidationResult($"Property '{_otherPropertyName}' does not exist");
);
var otherPropertyValue = otherProperty.GetValue(validationContext.ObjectInstance, null);
if (!_compareValue.Equals(otherPropertyValue))
{
return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}
It should give you a rough idea on what you can do and you can change the actual validation to however you like. You can then use it like a normal attribute e.g.
[RequiredIfOtherProperty("SomeProperty", "ValueToCompareWith")]
I believe mvcfoolproof will work for this situation [https://foolproof.codeplex.com/][1]
It is also available on nuget. It adds additional validation attributes such as
[RequiredIf]
[RequiredIfNot]
[RequiredIfTrue]
[RequiredIfFalse]
[RequiredIfEmpty]
[RequiredIfNotEmpty]
[RequiredIfRegExMatch]
[RequiredIfNotRegExMatch]
It is very simple to use.

Set RegularExpression Dynamically in Model

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'));
}
}

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