C# 基于其他值的自定义验证

C# 基于其他值的自定义验证,c#,asp.net-mvc,validation,validationattribute,C#,Asp.net Mvc,Validation,Validationattribute,我为餐馆做了一张预订表,上面写着餐馆的名字、用餐日期和人数 我有一个预订课程,有一个ID,一个餐厅ID,一个日期和一些人: public class Booking { public int Id { get; set; } public int IDRestaurant{ get; set; } [CustomPlaceValidator] public int Nbpeople { get; set; } [CustomDateValidator]

我为餐馆做了一张预订表,上面写着餐馆的名字、用餐日期和人数

我有一个预订课程,有一个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;

    }

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


如何获得其他属性的值以进行验证?

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

其次,验证属性不应执行类似于进行数据库查询的操作。控制员应单独负责与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");
    }
}
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; }
}

然后,在预订的post操作中,只需在检查
ModelState.IsValid

之前调用此函数即可,您要求的是跨属性验证。如果您不强烈反对在数据对象上实现接口,则应了解以下内容:

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

公共类SmallRectangle:IValidatableObject
{
公共uint宽度{get;set;}
公共单元高度{get;set;}
公共IEnumerable验证(ValidationContext ValidationContext)
{
可变面积=宽度*高度;
如果(面积>37)
{
返回新的ValidationResult($“矩形太大。”);
}
}
}
选择
ValidationAttribute
中的
IsValid
函数的第二个参数为您提供了
ValidationContext
,它具有属性
ObjectInstance
,您可以将该属性转换为对象类型并访问其其他成员。但是,这将使您的验证属性特定于您的类。我一般不建议这样做

您也可以选择完全使用不同的验证方法,例如使用FluentValidations等验证库,请参阅:

不同的观点
最后但并非最不重要的一点是,我想指出,通常应该使用验证来验证数据的完整性。要求座位超过可用座位数的预订请求并非无效。它不能被批准,但这是一个有效的请求,不幸的是,它的回答将是否定的。在我看来,给出负面结果不是验证的责任,而是业务逻辑。

我在考虑这个问题:

我猜是这样的:

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");
    }
}
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个答案之后,我意识到这并不一定是明智的。如果您希望客户端验证需要数据库访问,请使用
RemoteAttribute
进行ajax调用-。不要将数据库访问代码放入
ValidationAttribute