使用自定义DataAnnotation属性比较两个属性?

时间:2013-07-18 20:37:37

标签: validation asp.net-mvc-4 properties model data-annotations

我有一个自定义模型,其中包含一些DateTime值,以及一个用于比较这些值的自定义DataAnnotation

以下是带注释的属性:

[Required]
[DataType(System.ComponentModel.DataAnnotations.DataType.Date)]
[Display(Name = "Start Date")]
public DateTime StartTime { get; set; }

[DataType(System.ComponentModel.DataAnnotations.DataType.Date)]
[Display(Name = "End Date")]
[CompareTo(this.StartTime, CompareToAttribute.CompareOperator.GreaterThanEqual)]
public DateTime? EndTime { get; set; }

CompareTo属性是有问题的属性。我收到一个错误:

Keyword 'this' is not available in the current context

我尝试只在注释中放置StartTime而没有运气。如何从同一模型类传递属性值?

4 个答案:

答案 0 :(得分:5)

如果有人仍然想知道如何比较两个日期并在验证DataAnnotation中使用它,您只需添加一个扩展方法,将比较开始日期和结束日期,如下所示。

假设这是你的班级:

using System;
using System.ComponentModel.DataAnnotations;

namespace Entities.Models
{
    public class Periode
    {
        [Key]
        public int PeriodeID { get; set; }

        public string Name { get; set; }

        [DataType(DataType.Date)]
        [Display(Name ="Start Date")]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime StartDate { get; set; }

        [DataType(DataType.Date)]
        [Display(Name = "End Date")]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime EndDate { get; set; }
    }
}

您只需将以下类添加为验证器:

namespace Entities.Models
{

    public class StartEndDateValidator : ValidationAttribute
    {
        protected override ValidationResult
                IsValid(object value, ValidationContext validationContext)
        {
            var model = (Models.Periode)validationContext.ObjectInstance;
            DateTime EndDate = Convert.ToDateTime(model.EndDate);
            DateTime StartDate = Convert.ToDateTime(value);

            if (StartDate > EndDate)
            {
                return new ValidationResult
                    ("The start date must be anterior to the end date");
            }
            else
            {
                return ValidationResult.Success;
            }
        }
    }
}

然后你需要在StartDate上添加DataAnnotation,如下所示

namespace Entities.Models
{
    public class Periode
    {
        [Key]
        public int PeriodeID { get; set; }

        public string Name { get; set; }

        [DataType(DataType.Date)]
        [Display(Name ="Start Date")]
        // You need to add the following line
        [StartEndDateValidator]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime StartDate { get; set; }

        [DataType(DataType.Date)]
        [Display(Name = "End Date")]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime EndDate { get; set; }
    }
}

答案 1 :(得分:3)

  

我尝试在注释中只放置StartTime而没有运气。怎么样   我可以从同一个模型类中传入属性值吗?

这是不可能的,因为属性是在编译时被烘焙到程序集中的元数据。这意味着您只能将 CONSTANT 参数传递给属性。是的,这是一个极限的限制,因为为了执行这样一个明显的验证,比如你的模型中的2个值你必须编写管道代码的gazzilion,例如我在这里说明的内容:https://stackoverflow.com/a/16100455/29407我意思是,你将不得不使用反射!加油微软!你认真吗?

或者只是删除数据注释的废话并开始以正确的方式进行验证:使用FluentValidation.NET。它允许您以非常优雅的方式表达验证规则,它非常integrates with ASP.NET MVC并允许您单独unit test验证逻辑。它也不依赖于反射,所以它非常快。我已经对它进行了基准测试并在非常繁忙的流量生产应用程序中使用它。

当您开始编写比 Hello World 稍微复杂且需要比您更复杂的验证逻辑的应用程序时,与命令式验证规则相比,数据注释不会削减芥末会有一个 Hello World 应用程序。

答案 2 :(得分:1)

我喜欢哈森的回答。

与Hassen的例子相同,但建议: 1)如果结束日期是可选的,则在没有结束日期时中止。 2)在结束日期放置验证器,以防用户仅更改结束日期

数据注释:

[Required]
[Display(Name = "Start Effective Date", Description = "Start Date")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}")]
[DataType(DataType.Date)]
[StartDateValidator]
public DateTime StartEffectiveDate { get; set; }

[Display(Name = "End Effective Date", Description = "End Date")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}")]
[DataType(DataType.Date)]
[EndDateValidator]
public DateTime? EndEffectiveDate { get; set; }

代码:

public class StartDateValidator : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var model = (CostCenterAllocationHeader)validationContext.ObjectInstance;
        if (model.EndEffectiveDate == null)  // Abort if no End Date
            return ValidationResult.Success;

        DateTime EndDate = model.EndEffectiveDate.GetValueOrDefault();
        DateTime StartDate = Convert.ToDateTime(value);  // value = StartDate

        if (StartDate > EndDate)
            return new ValidationResult("The start date must be before the end date");
        else
            return ValidationResult.Success;
    }
}

public class EndDateValidator : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var model = (CostCenterAllocationHeader)validationContext.ObjectInstance;
        if (model.EndEffectiveDate == null)  // Abort if no End Date
            return ValidationResult.Success;

        DateTime EndDate = Convert.ToDateTime(value); // value = EndDate
        DateTime StartDate = model.StartEffectiveDate; 

        if (StartDate > EndDate)
            return new ValidationResult("The start date must be before the end date");
        else
            return ValidationResult.Success;
    }
}

本来会对Hassen的答案发表评论,但声誉不足。

答案 3 :(得分:0)

public class StartDateValidator : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var model = (CostCenterAllocationHeader)validationContext.ObjectInstance;
        if (model.EndEffectiveDate == null)  // Abort if no End Date
            return ValidationResult.Success;

        DateTime EndDate = model.EndEffectiveDate.GetValueOrDefault();
        DateTime StartDate = Convert.ToDateTime(value);  // value = StartDate

        if (StartDate > EndDate)
            return new ValidationResult("The start date must be before the end date");
        else
            return ValidationResult.Success;
    }
}

在上面的示例中,有一个问题,该解决方案不能用作验证日期的通用解决方案。因为以下行的类型转换不是通用的

var model = (CostCenterAllocationHeader)validationContext.ObjectInstance;

这意味着验证只能应用于特定模型 “ costCenterAllocationHeader” 。需要执行的操作是将成员名称传递给验证器的构造函数,并使用反射从ValidationContext中获取值。通过这种方法,我们可以将此属性用作通用解决方案,并且可以在任何ViewModel中应用。

相关问题