Custom Validation rule not working .NET 5 MVC - validation

I'm trying to apply a custom validation rule to my MVC model, to ensure that the value in FieldA will not be less than the value in FieldB when both fields are not null. They are nullable in my model.
Custom Validation code, "NotLessThan"
public class NotLessThan : ValidationAttribute
{
private readonly string testedValue;
public NotLessThan(string testedValue)
{
this.testedValue = testedValue;
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var compareObject = context.ObjectType.GetProperty(this.testedValue);
var compareValue = compareObject.GetValue(context.ObjectInstance, null);
if ((decimal)value >= (decimal)compareValue)
{
return ValidationResult.Success;
}
return new ValidationResult(FormatErrorMessage(context.DisplayName));
}
}
Model Code
[NotLessThan("FieldB", ErrorMessage = "FieldA cannot be less than FieldB")]
public decimal? FieldA { get; set; }
public decimal? FieldB { get; set; }
View Code
#Html.TextBoxFor(t => t.FieldA)
#Html.ValidationMessageFor(t => t.FieldA)
I have other validation on other fields in this model, standard stuff like Required fields and so on. All that other validation triggers and returns the expected validation messages. However, even if I satisfy all my other required fields, my "NotLessThan" custom attribute does not fire, even with failing input.
What am I doing wrong?

Related

ASP.net MVC4 validating elements of a drop down in the model?

How can I validate the values assigned to elements of a dropdown list? Normally I would assign ranges in the model and that field would be validated. However, if I have something like this I am not sure how to handle it.
Model
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Value { get; set; }
public DateTime Given { get; set; }
public TimeSpan TimeGiven { get; set; }
public string Phase { get; set; }
public bool Active { get; set; }
public int PersonId { get; set; }
}
The name in the model is a dropdown list of different products. I am not sure how to handle the validation for the Value since the different products will have different ranges. For example, Product Named X will have a valid range of 25-30 where product Y will have a valid range of .01 - .5. The Person can have many products assigned so I have a one to many relationship set up with Person and Product.
Is there a way to validate the value based on what product they select X, Y? I will have approximately 40 different products so Ideally I could do this without having to having a separate model for each product.
You can validate using custom business rules with a ValidationAttribute
It is very straightforward you just need to do the following:
Create a class that inherits from ValidationAttribute and override the IsValid method.
Decorate your property with the attribute you just created.
For example:
[AttributeUsage(AttributeTargets.Property, AllowMultiple =false, Inherited = false)]
public class MyBusinessRuleValidation: ValidationAttribute
{
protected override ValidationResult IsValid(object v, ValidationContext validationContext)
{
var Name = (string)v //since we decorated the property Name with this attribute;
//retrieve Value's value using validationContext
var value = (decimal) validationContext.ObjectType.GetProperty("Value").GetValue(validationContext.ObjectInstance, null);
//check whether you need to exit with error
if( name == ProductX) {
if(value > 10 && value < 25)
return new ValidationResult(ErrorMessage);
}
return ValidationResult.Success;
}
}
Use the validator:
public class Product
{
public int Id { get; set; }
[MyBusinessRuleValidation(ErrorMessage="Some ugly error")]
public string Name { get; set; }
....
}

Validation a public List<KeyValuePair<int, string>> in MVC3

I have the following property in my Model :
[StringLength(100, ErrorMessage = "Must be less than 100 Chars", MinimumLength = 3)]
public List<KeyValuePair<int, string>> Authors { get; set; }
How can I Validate each string into the above list with DataAnnotation Validation attribute in MVC3 ?
Is it possible at all ?
Custom validation to the rescue! You need to do the following:
Implement the IValidatableObject interface
Implement the IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
Implement your logic to determine that each string has less than 100 characters
Here's the code
public class YourModel : IValidatableObject
{
public List<KeyValuePair<int, string>> Authors { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
foreach(KeyValuePair<int, string> myKvp in Authors)
{
if(myKvp.Value.Length >= 100)
{
yield return new ValidationResult("Must be less than 100 characters");
}
}
}
}
That way you can do a call to if(Model.IsValid) in your controller action, and return any errors that are reported. If your KeyValuePair entries are referring to a specific entity you can even do something like:
yield return new ValidationResult("Must be less than 100 characters", new string[] { myKvp.Key.ToString() });
You'd need to tailor it to fit the ID of the attribute on your page. This way, the error message could be specific to an input on your page.
I override IValidatableObject in many places as there's many cases where I do validation that's dependent on the state of my object. Your case is a bit different, but it's certainly do-able as can be seen from the above example. (All that's off the top of my head, however, so may not be perfect!)

ASP.NET MVC 3 - Setting null field in DTO when binding to HTTP POST instead of failing

I have an HttpPost controller action that takes in a simple form DTO object.
[HttpPost]
public ViewResult Index(ResultQueryForm queryForm)
{
...
}
public class ResultQueryForm
{
public DateTime? TimestampStart { get; set; }
public DateTime? TimestampEnd { get; set; }
public string Name { get; set; }
}
The DTO object has nullable datetime fields used to create a range. The reason that it is set to nullable is because the form that is bound to the model is a query form, and the user doesn't have to enter a date value in the form.
The problem I'm running into is that if the user enters an invalid date, i would like the MVC default model binding to provide an error message. This happens flawlessly if I have a controller action that takes a DateTime? type as a argument, but since I'm passing a DTO that holds a DateTime? type the model binding appears to just set the DateTime? variable to null. This causes unexpected results.
Note:
[HttpPost]
public ViewResult Index(DateTime? startDate)
{
// If the user enters an invalid date, the controller action won't even be run because the MVC model binding will fail and return an error message to the user
}
Is there anyway to tell MVC model binding to "fail" if it can't bind the DateTime? value to the form DTO object, instead of just setting it to null? Is there a better way? Passing each individual form input to the controller is infeasible, due to the large amount of properties in the form/dto object (I've excluded many of them for easy reading).
You can validate your model in the controller action.
if(!Model.IsValid)
{
return View(); // ooops didn't work
}
else
{
return RedirectToAction("Index"); //horray
}
Of course you can put whatever you want in there, and return Json object if you want to display it on your page.
Also you need to add ValidateInput(true) up the top of your action method like this: [HttpPost, ValidateInput(true)]
I think you can create a custom ValidationAttribute for this.
[DateTimeFormat(ErrorMessage = "Invalid date format.")]
public DateTime? TimestampStart { get; set; }
[DateTimeFormat(ErrorMessage = "Invalid date format.")]
public DateTime? TimestampEnd { get; set; }
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class DateTimeFormatAttribute : ValidationAttribute
{
public override bool IsValid(object value) {
// allow null values
if (value == null) { return true; }
// when value is not null, try to convert to a DateTime
DateTime asDateTime;
if (DateTime.TryParse(value.ToString(), out asDateTime)) {
return true; // parsed to datetime successfully
}
return false; // value could not be parsed
}
}

Asp.Net Mvc 2 Custom Validation Issue

My question is regarding MVC 2 custom validation. I'm stuck on a particular issue and I'm unsure how to get around it. I'm fairly sure it's more figuring out how to do it logically and then implementing it in code.
So what we have is a metadata class for a product. Each product has a product product ID which is the PK, and obviously unique. Each product also has a product code which is also unique. Customers enter the product code however, but the nature of the code ensures that only one code is attached to one product so it will be unique.
Here is a snippet from the metadata class:
public partial class ProductMetadata
{
[DisplayName("Product Name")]
[Required(ErrorMessage = "Product Name is required.")]
public string ProductName { get; set; }
[DisplayName("Product Code")]
[Required(ErrorMessage = "Product Code is required.")]
[ProductCodeAlreadyExistsValidator(ErrorMessage = "This Product code is in use.")]
public string ProductCode { get; set; }
}
The 'ProductCodeAlreadyExistsValidator' works perfectly when creating a new product. The problem lies in editing an existing product as the validation is being performed on this attribute again, and it is finding itself in the database. This results in the validation failing.
Here is a snippet from the custom validator:
public class ProductCodeAlreadyExistsValidator : ValidationAttribute
{
private readonly object typeId = new object();
private const string defaultErrorMessage = "Product Code {0} is already present in the system.";
public ProductCodeAlreadyExistsValidator()
: base(defaultErrorMessage)
{
}
public override object TypeId
{
get
{
return typeId;
}
}
public string CustomerType { get; set; }
public string CustomerFriendlyType { get; set; }
public override string FormatErrorMessage(string roleName)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, roleName);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (!IsValid(value))
{
string errorMessage = string.Format(defaultErrorMessage, validationContext.MemberName, value as string);
return new ValidationResult(errorMessage, new string[] { validationContext.MemberName });
}
return null;
}
public override bool IsValid(object value)
{
bool alreadyPresent = false;
string ProductCode = value as string;
using (ModelContainer ctn = new ModelContainer())
{
alreadyPresent = ctn.Products.Where(t => t.ProductCode == ProductCode).Count() > 0;
}
return !alreadyPresent;
}
}
It might be a relatively simple fix however I seem to have hit a brick wall with it. Can anyone offer any advice?
Code seems ok to me. I think you need to identify if you are doing an insert or an update so the validation can ignore the checking when updating the field. You could check if the ID of the item editted is the same ID found by the code, it would be identified as a Editting.

Validation Attributes MVC 2 - checking one of two values

Could someone help me with this issue. I'm trying to figure out how to check two values on a form, one of the two items has to be filled in. How do I do a check to ensure one or both of the items have been entered?
I'm using viewmodels in ASP.NET MVC 2.
Here's a little snip of code:
The view:
Email: <%=Html.TextBoxFor(x => x.Email)%>
Telephone: <%=Html.TextBoxFor(x => x.TelephoneNumber)%>
The viewmodel:
[Email(ErrorMessage = "Please Enter a Valid Email Address")]
public string Email { get; set; }
[DisplayName("Telephone Number")]
public string TelephoneNumber { get; set; }
I want either of these details to be provided.
Thanks for any pointers.
You can probably do this in much the same way as the PropertiesMustMatch attribute that comes as part of the File->New->ASP.NET MVC 2 Web Application.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class EitherOrAttribute : ValidationAttribute
{
private const string _defaultErrorMessage = "Either '{0}' or '{1}' must have a value.";
private readonly object _typeId = new object();
public EitherOrAttribute(string primaryProperty, string secondaryProperty)
: base(_defaultErrorMessage)
{
PrimaryProperty = primaryProperty;
SecondaryProperty = secondaryProperty;
}
public string PrimaryProperty { get; private set; }
public string SecondaryProperty { get; private set; }
public override object TypeId
{
get
{
return _typeId;
}
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
PrimaryProperty, SecondaryProperty);
}
public override bool IsValid(object value)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
object primaryValue = properties.Find(PrimaryProperty, true /* ignoreCase */).GetValue(value);
object secondaryValue = properties.Find(SecondaryProperty, true /* ignoreCase */).GetValue(value);
return primaryValue != null || secondaryValue != null;
}
}
The key part of this function is the IsValid function that determines if one of the two parameters has a value.
Unlike normal Property-based attributes, this is applied to the class level and can be used like so:
[EitherOr("Email", "TelephoneNumber")]
public class ExampleViewModel
{
[Email(ErrorMessage = "Please Enter a Valid Email Address")]
public string Email { get; set; }
[DisplayName("Telephone Number")]
public string TelephoneNumber { get; set; }
}
You should be able to add as many as these as you need per form, but if you want to force them to enter a value into one of more than two boxes (Email, Telephone or Fax for example), then you would probably be best changing the input to be more an array of values and parse it that way.

Resources