【问题标题】:Custom validation based on other value基于其他值的自定义验证
【发布时间】:2017-12-04 12:18:11
【问题描述】:

我制作了一个餐厅预订表,其中要求提供餐厅名称、用餐日期和人数。

我有一个预订班级,其中有一个 ID、餐厅 ID、日期和人数:

public class Booking
{
    public int Id { get; set; }
    public int IDRestaurant{ get; set; }
    [CustomPlaceValidator]
    public int Nbpeople { get; set; }
    [CustomDateValidator]
    public DateTime Date { get; set; }
}

还有一个Resto类,它有一个ID、一个名字、一个电话号码和一个号码表:

public class Resto
{
    public int Id { get; set; }
    [Required(ErrorMessage = "Le nom du restaurant doit être saisi")]
    public string Nom { get; set; }
    [Display(Name = "Téléphone")]
    [RegularExpression(@"^0[0-9]{9}$", ErrorMessage = "Le numéro de téléphone est incorrect")]
    public string Telephone { get; set; }
    [Range(0, 9999)]
    public int Size { get; set; }
}

我想对每个新预订进行验证,以确认餐厅未满。 为此,在验证预订的“人数”字段时,我需要“餐厅名称”字段的值和“日期”字段的值,然后检索该餐厅在该日期的所有预订,并检查人数之和是否远低于餐厅的容量。

public class CustomPlaceValidator : ValidationAttribute
{
    private IDal dal = new Dal();
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        int nb = 0;
        if (dal.GetAllBooking() != null)
        {
            foreach (var booking in dal.GetAllBooking())
                nb += booking.Nbpeople;
            if (nb ..... ) return ValidationResult.Success;
            return new ValidationResult("The restaurant is full for this date.");
        }
        return ValidationResult.Success;

    }

}

(这是草稿,测试显然没有完成)

我怎样才能获得其他属性的价值来进行验证?

【问题讨论】:

标签: c# asp.net-mvc validation validationattribute


【解决方案1】:

您要求的是跨属性验证。如果您不强烈反对在数据对象上实现接口,则应查看以下内容:

https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.ivalidatableobject.aspx

一个小矩形类的简单示例实现,我们希望其面积不超过 37(无论该单位是什么)。

public class SmallRectangle : IValidatableObject
{
    public uint Width { get; set; }
    public uint Height { get; set; }
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var area = Width * Height;
        if (area > 37)
        {
            yield return new ValidationResult($"The rectangle is too large.");
        }
    }
}

替代品

ValidationAttributeIsValid 函数的第二个参数为您提供ValidationContext,它具有属性ObjectInstance,您可以将其强制转换为您的对象类型并访问其其他成员。但是,这将使您的验证属性特定于您的类。我通常会建议不要这样做。

您还可以选择完全使用不同的验证方法,例如使用 FluentValidations 等验证库,请参阅: https://github.com/JeremySkinner/FluentValidation

不同的视角

最后但并非最不重要的一点是,我想指出,通常应该使用验证来验证数据的完整性。请求的座位多于可用座位的预订请求并非无效。它不能被授予,但它是一个有效的请求,不幸的是,它会得到否定的结果。在我看来,给出否定结果不是验证的责任,而是业务逻辑的责任。

【讨论】:

  • 感谢您的解决方案和让我意识到这不一定是正确方法的评论!
【解决方案2】:

我在看这个问题:Group validation messages for multiple properties together into one message asp.net mvc

我的猜测是这样的:

public class Booking
{
    public int Id { get; set; }
    public int IDRestaurant{ get; set; }
    [CustomPlace("IDRestaurant", "Date", ErrorMessage = "the restaurant is full")]
    public int Nbpeople { get; set; }
    [CustomDateValidator]
    public DateTime Date { get; set; }
}

以及自定义验证:

public class CustomPlaceAttribute : ValidationAttribute
{
    private readonly string[] _others
    public CustomPlaceAttribute(params string[] others)
    {
        _others= others;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
           // TODO: validate the length of _others to ensure you have all required inputs
           var property = validationContext.ObjectType.GetProperty(_others[0]);
           if (property == null)
           {
               return new ValidationResult(
                string.Format("Unknown property: {0}", _others[0])
               );
           }
           // This is to get one of the other value information. 
           var otherValue = property.GetValue(validationContext.ObjectInstance, null);

           // TODO: get the other value again for the date -- and then apply your business logic of determining the capacity          
    }
}

但是,为validationAttribute 进行数据库调用感觉有点混乱

【讨论】:

  • 谢谢,但实际上,在阅读了3个答案后,我意识到这不一定是明智的
【解决方案3】:

这不适用于验证属性。首先,验证属性应该是独立的,或者至少是自包含的。由于这里的逻辑取决于两个不同的属性(人数和预订日期),因此验证属性需要对领域有太多了解才能执行必要的验证。换句话说,它是不可重用的,如果它不可重用,那么使用属性就没有意义了。

其次,验证属性不应该做诸如进行数据库查询之类的事情。单独的控制器应该负责与您的 DAL 一起工作。当您开始在应用程序中乱扔数据库访问时,您将在很短的时间内开始遇到各种问题。如果您使用 DI 容器将 DAL 注入到需要去的地方,那么在控制器之外使用它的问题会更小,重要的是,属性确实不能很好地与依赖注入配合使用。你可以让它与一些 DI 容器一起工作,但这绝非易事,而且你以后可能会后悔。所以,再一次,这真的不应该是验证属性处理的事情。

我认为最好的方法是在控制器上简单地创建一个私有/受保护的方法来处理此验证。比如:

public void ValidateCapacity(Booking booking)
{
    var restaurant = dal.GetRestaurant(booking.IDRestaurant);
    var existingBookings = dal.GetBookings(booking.IDRestaurant, booking.Date);
    var available = restaurant.Size - existingBookings.Sum(b => b.Nbpeople);
    if (booking.Nbpeople > available)
    {
        ModelState.AddModelError("Nbpeople", "There is not enough capacity at the restaurant for this many people on the date you've selected");
    }
}

然后,在您的预订后操作中,只需在检查 ModelState.IsValid 之前调用它。

【讨论】:

  • 谢谢,确实这似乎更适合我想做的事情!
猜你喜欢
  • 2020-10-16
  • 2023-01-11
  • 2022-01-23
  • 2019-11-03
  • 2017-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多