如何避免 FluentValidation 中的重复外部调用

How to avoid repeated external calls in FluentValidation

我正在为需要多次 similar/identical 数据库调用的 class(例如汽车)编写验证。

RuleFor(c => c.Id).MustAsync(async (car, id, context, cancellation) => 
{
    return await _carRepository.Get(id) != null;
}).WithMessage("Car with id '{PropertyValue}' does not exist!");

RuleFor(c => c.Model).MustAsync(async (car, model, context, cancellation) => 
{
    var expectedModel = (ModelType)context.ParentContext.RootContextdata["ExpectedModel"]
    var databaseCar = await _carRepository.Get(car.Id); // Repeated database call
    return databaseCar.Model == expectedModel;
}).WithMessage('Stored car does not have the expected model.');

理想情况下,我会执行一次此调用,但我收集到的结果是不建议将结果作为成员存储在验证器实例上,并且使用添加到上下文的数据库结果覆盖 ValidateAsync(类似于 ExpectedModel 在上面的例子中)导致相当笨拙的代码来检索它。

我是不是漏掉了什么?

我认为检查具有相同 ID 的现有项目不是验证问题。

如果你必须这样做。在您的存储库中创建一个方法,专门以优化的方式检查它。只有 select ID 列,所以至少你没有加载和材料化整个实体。

一个快速的解决方案可能是在您的存储库 class 上添加某种 memoization/caching,以便在同一上下文中对同一辆车的多个请求(例如 HTTP 请求)将记住并 return 同一个对象,不需要多次往返。但可能有更好的方法。

需要考虑不同级别的验证。正如 Jammer 指出的那样,FluentValidation 通常用于验证给定模型的一致性:客户端是否向我发送了表面上显示为有效请求的内容?在给定当前数据状态的情况下确定该请求是否有效是人们经常以不同方式进行的另一个验证级别。

您可以两全其美的一种方法是创建一个新的 class 来表示给定的汽车模型和您的应用程序验证它所需的一切。

public class ValidCar
{
    public CarModel Model {get; set;}
    public CarEntity Entity {get; set;}
}

首先你 assemble 将你需要的所有数据放入一个 new ValidCar,然后你可以在这个新模型上使用 FluentValidation 规则来确保它确实有效。

这种方法的一个好处是您可以让您的业务逻辑方法需要 ValidCar 作为参数,而不仅仅是 CarModel。这使得意外忘记在某些代码路径中验证汽车变得非常困难,并且它预先打包了可能对您计划使用的大部分业务级逻辑有用的数据。