我正在尝试使用FluentValidation nuget包验证请求。我使用的是一个winform,它有几个文本框字段,如下面的方法所示。当用户单击确认按钮时,系统将尝试使用文本框中的数据参数创建我的车辆类的新实例。我想使用验证检查器来查看他们正在创建的车辆实例是否有效。我遇到的问题是,当有一个文本框需要转换为in或decimal才能创建实例时,我会收到错误消息Input string was not in a correct format.
。有没有一种方法可以在检查验证之前将int/decimal值作为字符串传递,然后将其作为正确的形式传回?
这是我的按钮点击方法:
protected override void CheckInputFields()
{
string registration = textBoxRegistrationNumber.Text;
string make = textBoxMake.Text;
string model = textBoxModel.Text;
string year = textBoxYear.Text;
string cost = textBoxHireCost.Text;
ValidationAddVehicle validator = new ValidationAddVehicle();
//The issue is here. When the cost or year fields are empty, it cant convert them to decimal/int.
//Which means that the validation checker cannot do the .NotEmpty() method
ValidationResult results = validator.Validate(Vehicle.New(registration, make, model, Convert.ToDecimal(cost), Convert.ToInt32(year)));
if (results.IsValid == false)
{
foreach (ValidationFailure error in results.Errors)
MessageBox.Show($"{error.PropertyName} : {error.ErrorMessage}");
return;
}
else
{
MessageBox.Show("Success");
return;
}
}
以下是我创建车辆的方法:
public static Vehicle New(string registration, string make, string model, decimal cost, int year)
{
return new Vehicle
{
Registration = registration,
Model = model,
Make = make,
Year = year,
Cost = cost
};
}
这是我的验证检查器:
public class ValidationAddVehicle : AbstractValidator<Vehicle>
{
public ValidationAddVehicle()
{
//Registration
RuleFor(v => v.Registration)
.Cascade(CascadeMode.Stop)
.NotEmpty().WithMessage("{PropertyName} is empty")
.Length(2, 10).WithMessage("Length ({TotalLength}) of {PropertyName} is invalid")
.Must(IsValidRegistration).WithMessage("{PropertyName} contains invalid characters")
.Must(Exists).WithMessage("{PropertyName} already exists");
//Make
RuleFor(v => v.Make)
.Cascade(CascadeMode.Stop)
.NotEmpty().WithMessage("{PropertyName} is empty")
.Length(1, 50).WithMessage("Length ({TotalLength}) of {PropertyName} is invalid")
.Must(IsValidMake).WithMessage("{PropertyName} contains invalid characters");
//Model
RuleFor(v => v.Model)
.Cascade(CascadeMode.Stop)
.NotEmpty().WithMessage("{PropertyName} is empty")
.Length(1, 50).WithMessage("Length ({TotalLength}) of {PropertyName} is invalid")
.Must(IsValidModel).WithMessage("{PropertyName} contains invalid characters");
//Cost
RuleFor(v => v.Cost)
.Cascade(CascadeMode.Stop)
.NotEmpty().WithMessage("{PropertyName} is empty")
.Must(IsValidCost).WithMessage("{PropertyName} contains invalid characters")
.Must(CostValue).WithMessage("{PropertyName} must be greater than zero");
//Year
RuleFor(v => v.Year)
.Cascade(CascadeMode.Stop)
.NotEmpty().WithMessage("{PropertyName} is empty")
.Must(IsValidYear).WithMessage("{PropertryName} is an invalid value")
.Must(Range).WithMessage("{PropertryName} must be between 1886 and {DateTime.Now.AddYears(1).ToString()}");
}
protected bool IsValidRegistration(string registration) => String.Concat(registration.Where(r => !Char.IsWhiteSpace(r))) != "" && registration.All(Char.IsLetterOrDigit);
protected bool Exists(string registration) => !Business.VehicleList.Any(x => x.Registration == registration);
protected bool IsValidMake(string make) => String.Concat(make.Where(m => !Char.IsWhiteSpace(m))) != "" && make.All(Char.IsLetter);
protected bool IsValidModel(string model) => String.Concat(model.Where(m => !Char.IsWhiteSpace(m))) != "" && model.All(Char.IsLetterOrDigit);
protected bool IsValidCost(decimal cost) => String.Concat(cost.ToString().Where(c => !Char.IsWhiteSpace(c))) != "" && Regex.IsMatch(cost.ToString(), @"^-?[0-9]*.?[0-9]+$");
protected bool CostValue(decimal cost) => cost > 0;
protected bool IsValidYear(int year) => String.Concat(year.ToString().Where(y => !Char.IsWhiteSpace(y))) != "" && Regex.IsMatch(year.ToString(), @"^[0-9]");
protected bool Range(int year) => year.ToString().Length == 4 && Convert.ToDateTime($"{year},1,1") <= DateTime.Now.AddYears(1) && year >= 1886;
}
有没有一种方法可以让我首先将成本和年份作为字符串传递给车辆类别,检查其有效性,然后将其转换为正确的数据类型,或者我可以使用其他方法来实现这一点?
有没有一种方法可以将int/decimal值作为字符串传递在我检查之前,请验证它们,然后将它们作为正确的返回类型
有点。Transform允许您进行转换以进行验证:
Transform(v => v.Cost, v => Convert.ToDecimal(v)
// validation rules
不过,这不会返回转换后的值,您可能需要使用相关规则来确保可以执行转换,例如,检查是否为null或空白,然后进行转换。
我的想法是退一步考虑一下这个方法。这是验证问题,还是模型绑定问题?请考虑ASP.NET中的http请求管道如何处理相同的场景。后者会将此视为模型绑定错误。如果您带头在调用FluentValidation之前进行这些转换,那么您就可以将验证器的范围扩展到强类型模型,而不会出现这个问题。
如果你想在FluentValidation中完成所有这些,这样你就可以为输入获得一个统一的错误源,那么你会觉得你有一个具有字符串属性的输入模型。验证输入模型,如果验证通过,则可以将输入模型映射到您的服务/数据/任何模型,并在其中包括类型转换。FluentValidation是关于确保模型有效,而不是验证和丰富模型。
如果你想坚持验证强类型模型,那么你需要在验证器之外进行转换。
考虑级联使用(可以在验证器/全局级别设置,而不是为每个规则设置(,并使用自定义语言管理器来避免为每个验证器指定相同的自定义消息。