Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP .NET MVC 3 Data Annotations GreaterThan LowerThan for DateTime and int

I would like to know what is the easiest way to have a "Greater Than" & "Lower Than" validation on a ASP.NET MVC 3 form?

I use unobtrusive JavaScript for client validation. I have two DateTime properties (StartDate & EndDate) and I need a validation to be sure that the EndDate is greater than the StartDate. I have another similar case with another form on which I have a MinValue (int) & MaxValue (int).

Does this type of validation exist by default? Or does someone know an article which explains how to implement it?

like image 432
user742898 Avatar asked May 07 '11 10:05

user742898


3 Answers

Could look at the dataannotationsextensions it does Min/Max for int

Also have a look at a foolproof validation it inlcudes GreaterThan comparison for numeric/datetime etc

like image 79
GraemeMiller Avatar answered Nov 15 '22 00:11

GraemeMiller


You can simply do this with custom validation.

[AttributeUsage(AttributeTargets.Property, AllowMultiple=true)]
    public class DateGreaterThanAttribute : ValidationAttribute
    {
        string otherPropertyName;

        public DateGreaterThanAttribute(string otherPropertyName, string errorMessage)
            : base(errorMessage)
        {
            this.otherPropertyName = otherPropertyName;
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            ValidationResult validationResult = ValidationResult.Success;
            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);
                // Let's check that otherProperty is of type DateTime as we expect it to be
                if (otherPropertyInfo.PropertyType.Equals(new DateTime().GetType()))
                {
                    DateTime toValidate = (DateTime)value;
                    DateTime referenceProperty = (DateTime)otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
                    // if the end date is lower than the start date, than the validationResult will be set to false and return
                    // a properly formatted error message
                    if (toValidate.CompareTo(referenceProperty) < 1)
                    {
                        validationResult = new ValidationResult(ErrorMessageString);
                    }
                }
                else
                {
                    validationResult = new ValidationResult("An error occurred while validating the property. OtherProperty is not of type DateTime");
                }
            }
            catch (Exception ex)
            {
                // Do stuff, i.e. log the exception
                // Let it go through the upper levels, something bad happened
                throw ex;
            }

            return validationResult;
        }
}

and use it in model like

 [DisplayName("Start date")]
    [DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]        
    public DateTime StartDate { get; set; }

    [DisplayName("Estimated end date")]
    [DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
    [DateGreaterThan("StartDate", "End Date end date must not exceed start date")]
    public DateTime EndDate { get; set; }

This works well with server side validation.For client side validaion you can write the method like GetClientValidationRules like

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 dateGreaterThanRule = new ModelClientValidationRule();
            dateGreaterThanRule.ErrorMessage = errorMessage;
            dateGreaterThanRule.ValidationType = "dategreaterthan"; // This is the name the jQuery adapter will use
            //"otherpropertyname" is the name of the jQuery parameter for the adapter, must be LOWERCASE!
            dateGreaterThanRule.ValidationParameters.Add("otherpropertyname", otherPropertyName);

            yield return dateGreaterThanRule;
        }

Now simply in view

$.validator.addMethod("dategreaterthan", function (value, element, params) {

    return Date.parse(value) > Date.parse($(params).val());
});
$.validator.unobtrusive.adapters.add("dategreaterthan", ["otherpropertyname"], function (options) {
    options.rules["dategreaterthan"] = "#" + options.params.otherpropertyname;
    options.messages["dategreaterthan"] = options.message;
});

You can find more details in this link

like image 23
S.p Avatar answered Nov 15 '22 00:11

S.p


I don't know if writing your own validator class is the "easiest" way, but that's what I did.

Usage:

<DataType(DataType.Date)>
Public Property StartDate() As DateTime


<DataType(DataType.Date)>
<DateGreaterThanEqual("StartDate", "end date must be after start date")>
Public Property EndDate() As DateTime

Class:

<AttributeUsage(AttributeTargets.Field Or AttributeTargets.Property, AllowMultiple:=False, Inherited:=False)>
Public Class DateGreaterThanEqualAttribute
    Inherits ValidationAttribute

    Public Sub New(ByVal compareDate As String, ByVal errorMessage As String)
        MyBase.New(errorMessage)
        _compareDate = compareDate
    End Sub
    Public ReadOnly Property CompareDate() As String
        Get
            Return _compareDate
        End Get
    End Property
    Private ReadOnly _compareDate As String

    Protected Overrides Function IsValid(ByVal value As Object, ByVal context As ValidationContext) As ValidationResult
        If value Is Nothing Then
            ' no need to do or check anything
            Return Nothing
        End If
        ' find the other property we need to compare with using reflection
        Dim compareToValue = Nothing
        Dim propAsDate As Date
        Try
            compareToValue = context.ObjectType.GetProperty(CompareDate).GetValue(context.ObjectInstance, Nothing).ToString
            propAsDate = CDate(compareToValue)
        Catch
            Try
                Dim dp As String = CompareDate.Substring(CompareDate.LastIndexOf(".") + 1)
                compareToValue = context.ObjectType.GetProperty(dp).GetValue(context.ObjectInstance, Nothing).ToString
                propAsDate = CDate(compareToValue)
            Catch
                compareToValue = Nothing
            End Try
        End Try

        If compareToValue Is Nothing Then
            'date is not supplied or not valid
            Return Nothing
        End If

        If value < compareToValue Then
            Return New ValidationResult(FormatErrorMessage(context.DisplayName))
        End If

        Return Nothing
    End Function

End Class
like image 1
Mindstorm Interactive Avatar answered Nov 15 '22 00:11

Mindstorm Interactive