Lets say I have a StartDate and an EndDate and I wnt to check if the EndDate is not more then 3 months apart from the Start Date
public class DateCompare : ValidationAttribute
{
public String StartDate { get; set; }
public String EndDate { get; set; }
//Constructor to take in the property names that are supposed to be checked
public DateCompare(String startDate, String endDate)
{
StartDate = startDate;
EndDate = endDate;
}
public override bool IsValid(object value)
{
var str = value.ToString();
if (string.IsNullOrEmpty(str))
return true;
DateTime theEndDate = DateTime.ParseExact(EndDate, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
DateTime theStartDate = DateTime.ParseExact(StartDate, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture).AddMonths(3);
return (DateTime.Compare(theStartDate, theEndDate) > 0);
}
}
and I would like to implement this into my validation
[DateCompare("StartDate", "EndDate", ErrorMessage = "The Deal can only be 3 months long!")]
I know I get an error here... but how can I do this sort of business rule validation in asp.net mvc
It's a late answer but I wanted to share it for others outhere. Here's how I've done it so that everything is validated using unobtrusive client validation:
Create an attribute class:
public class DateCompareValidationAttribute : ValidationAttribute, IClientValidatable
{
public enum CompareType
{
GreatherThen,
GreatherThenOrEqualTo,
EqualTo,
LessThenOrEqualTo,
LessThen
}
private CompareType _compareType;
private DateTime _fromDate;
private DateTime _toDate;
private string _propertyNameToCompare;
public DateCompareValidationAttribute(CompareType compareType, string message, string compareWith = "")
{
_compareType = compareType;
_propertyNameToCompare = compareWith;
ErrorMessage = message;
}
#region IClientValidatable Members
/// <summary>
/// Generates client validation rules
/// </summary>
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ValidateAndGetCompareToProperty(metadata.ContainerType);
var rule = new ModelClientValidationRule();
rule.ErrorMessage = ErrorMessage;
rule.ValidationParameters.Add("comparetodate", _propertyNameToCompare);
rule.ValidationParameters.Add("comparetype", _compareType);
rule.ValidationType = "compare";
yield return rule;
}
#endregion
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// Have to override IsValid method. If you have any logic for server site validation, put it here.
return ValidationResult.Success;
}
/// <summary>
/// verifies that the compare-to property exists and of the right types and returnes this property
/// </summary>
/// <param name="containerType">Type of the container object</param>
/// <returns></returns>
private PropertyInfo ValidateAndGetCompareToProperty(Type containerType)
{
var compareToProperty = containerType.GetProperty(_propertyNameToCompare);
if (compareToProperty == null)
{
string msg = string.Format("Invalid design time usage of {0}. Property {1} is not found in the {2}", this.GetType().FullName, _propertyNameToCompare, containerType.FullName);
throw new ArgumentException(msg);
}
if (compareToProperty.PropertyType != typeof(DateTime) && compareToProperty.PropertyType != typeof(DateTime?))
{
string msg = string.Format("Invalid design time usage of {0}. The type of property {1} of the {2} is not DateType", this.GetType().FullName, _propertyNameToCompare, containerType.FullName);
throw new ArgumentException(msg);
}
return compareToProperty;
}
}
Note: if you want to validate length of time, add another parameter to the constractor and change enumerator for this specific type of comparsion
Add the attributes to the field as folows: [DateCompareValidation(DateCompareValidationAttribute.CompareType.GreatherThenOrEqualTo, "This Date must be on or after another date", compareWith: "AnotherDate")]
Take a note how your generated html is changed. It should include your validation message, field name for the compare-to date, etc. The generated parms would start with "data-val-compare". You defined this "compare" when you set ValidationType="compare" in GetClientValidationRules method.
Now you need matching javascript code: to add an validation adapter and validation method. I used anonimous method here, but you don't have to. I recommend placing this code in a separate javascript file so that this file together with your attribute class become like a control and could be used anywhere.
$.validator.unobtrusive.adapters.add( 'compare', ['comparetodate', 'comparetype'], function (options) { options.rules['compare'] = options.params; options.messages['compare'] = options.message; } );
$.validator.addMethod("compare", function (value, element, parameters) {
// value is the actuall value entered
// element is the field itself, that contain the the value (in case the value is not enough)
var errMsg = "";
// validate parameters to make sure everyting the usage is right
if (parameters.comparetodate == undefined) {
errMsg = "Compare validation cannot be executed: comparetodate parameter not found";
alert(errMsg);
return false;
}
if (parameters.comparetype == undefined) {
errMsg = "Compare validation cannot be executed: comparetype parameter not found";
alert(errMsg);
return false;
}
var compareToDateElement = $('#' + parameters.comparetodate).get();
if (compareToDateElement.length == 0) {
errMsg = "Compare validation cannot be executed: Element to compare " + parameters.comparetodate + " not found";
alert(errMsg);
return false;
}
if (compareToDateElement.length > 1) {
errMsg = "Compare validation cannot be executed: more then one Element to compare with id " + parameters.comparetodate + " found";
alert(errMsg);
return false;
}
//debugger;
if (value && !isNaN(Date.parse(value))) {
//validate only the value contains a valid date. For invalid dates and blanks non-custom validation should be used
//get date to compare
var compareToDateValue = $('#' + parameters.comparetodate).val();
if (compareToDateValue && !isNaN(Date.parse(compareToDateValue))) {
//if date to compare is not a valid date, don't validate this
switch (parameters.comparetype) {
case 'GreatherThen':
return new Date(value) > new Date(compareToDateValue);
case 'GreatherThenOrEqualTo':
return new Date(value) >= new Date(compareToDateValue);
case 'EqualTo':
return new Date(value) == new Date(compareToDateValue);
case 'LessThenOrEqualTo':
return new Date(value) <= new Date(compareToDateValue);
case 'LessThen':
return new Date(value) < new Date(compareToDateValue);
default:
{
errMsg = "Compare validation cannot be executed: '" + parameters.comparetype + "' is invalid for comparetype parameter";
alert(errMsg);
return false;
}
}
return true;
}
else
return true;
}
else
return true;
});
This takes care only of client-side unobtrusive validation. If you need server side, you'd have to have some logic in the override of isValid method. Also, you can use Reflection to generate error message using display attributes, etc and make the message argument optional.
Entity:
[MetadataType(typeof(MyEntity_Validation))]
public partial class MyEntity
{
}
public class MyEntity_Validation
{
[Required(ErrorMessage="'Date from' is required")]
public DateTime DateFrom { get; set; }
[CompareDatesValidatorAttribute("DateFrom")]
public DateTime DateTo { get; set; }
}
Attribute:
public sealed class CompareDatesValidatorAttribute : ValidationAttribute
{
private string _dateToCompare;
private const string _errorMessage = "'{0}' must be greater or equal'{1}'";
public CompareDatesValidatorAttribute(string dateToCompare)
: base(_errorMessage)
{
_dateToCompare = dateToCompare;
}
public override string FormatErrorMessage(string name)
{
return string.Format(_errorMessage, name, _dateToCompare);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var dateToCompare = validationContext.ObjectType.GetProperty(_dateToCompare);
var dateToCompareValue = dateToCompare.GetValue(validationContext.ObjectInstance, null);
if (dateToCompareValue != null && value != null && (DateTime)value < (DateTime)dateToCompareValue)
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}
2. Password
Entity:
public string Password { get; set; }
[Compare("Password", ErrorMessage = "ConfirmPassword must match Password")]
public string ConfirmPassword { get; set; }
I hope it helps
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With