gpt4 book ai didi

c# - 在 .NET Core 中使用 FluentValidation 和依赖注入(inject)

转载 作者:行者123 更新时间:2023-12-03 23:52:04 25 4
gpt4 key购买 nike

我有一个 .NET Core Web Api 应用程序,它按以下方式排列 -

  • 注入(inject)业务服务的 Controller 层
  • 注入(inject)工作单元(与数据库交互)的业务服务
  • 业务服务也可能调用 FluentValidation 类
  • FluentValidation 将注入(inject)工作单元以执行数据库检查(存在等)

  • 说了这么多,这里就是一个例子。如果我想在系统中创建一个用户,我在“UsersController”内有一个名为“PostUser”的路由/方法。 “UsersController”注入(inject)“UserService”。 “UserService”有一个名为“CreateUser”的方法。所以在 Controller 的“PostUser”方法内部看起来像这样 -
    var user = _userService.CreateUser(user);

    现在在“CreateUser”方法中它看起来像这样 -
    UserValidation validation = new UserValidation(UnitOfWork, DatabaseOperation.Create);
    ValidationResult validationResult = await validation.ValidateAsync(user);

    因此 UnitOfWork 通过依赖注入(inject)传递给 UserService,然后传递给 FluentValidation 类“UserValidation”,这样验证类就可以执行数据库检查。我还将一个枚举传递给 UserValidation 类,以指定验证是针对更新还是创建。

    我要验证的 User 对象将具有诸如“Role”和“Company”之类的属性,并且我还为每个对象(RoleValidation 和 CompanyValidation)提供了单独的验证类。这两个验证类也将传入 UnitOfWork 以及这是创建还是更新。

    这是我的 UserValidation 类的示例-
    public class UserValidation : AbstractValidator<UserDTO> 
    {
    private IUnitOfWork _unitOfWork;
    public UserValidation(IUnitOfWork unitOfWork, DatabaseOperation databaseOperation)
    {
    _unitOfWork = unitOfWork;

    if (databaseOperation == DatabaseOperation.Create)
    {
    // Do Create specific validation
    }

    RuleFor(x => x.Company)
    .SetValidator(new CompanyValidator(_unitOfWork, databaseOperation));

    }
    }

    现在了解了所有这些,我想为我的“UserService”类创建单元测试。但我相信为了正确地做到这一点,在某些情况下我需要模拟 FluentValidation 类,正如您在我的“UserService”CreateUser 方法中看到的那样,我为我的验证实例化了具体类。因此,为了做到这一点,我必须为我的每个 fluentvalidation 类创建一个接口(interface),并将它们注入(inject)到使用它们的业务服务中。所以我在我的 Startup.cs 文件中做了以下操作 -
    services.AddScoped<IValidator<User>>(x => new UserValidation(x.GetRequiredService<IUnitOfWork>()));

    所以现在在这样做之后,我可以将 IValidator 注入(inject)到我的 UserService 构造函数中并使用它,而不是在我的 UserService 方法中实例化一个具体类。

    因此,我提出以下问题。
  • 在您看来,我已经构建了我的项目的方式,这是将依赖注入(inject)与 FluentValidation 一起使用并允许对服务方法进行单元测试以及对 FluentValidation 类进行单元测试的最佳方式吗?
  • 有没有更好的方法使用依赖注入(inject)和 FluentValidation 来完成所有这些,同时让 FluentValidation 类知道它是“创建”还是“更新”,而不是创建一个名为“UserCreateValidation”和“UserUpdateValidation”或将变量“DatabaseOperation”传递给验证器的构造函数?
  • 在尝试设置 FluentValidation DependencyInjection 时附加到 (2) 我在传递“DatabaseOperation”变量 services.AddScoped<IValidator<User>>(x => new UserValidation(x.GetRequiredService<IUnitOfWork>(), <How to figure out if its a create or an update>)); 时遇到问题
  • 最重要的是,我还必须在“Startup.cs”文件中添加两行,以创建“CompanyValidation”和“RoleValidation”的Scoped DependencyInjection,以便在“UserValidation”和这两个验证类中使用还将传递它是更新还是创建。

  • 任何帮助/建议将不胜感激。我真的被这个问题困住了。如果有人需要进一步澄清我面临的问题,请随时提问。

    谢谢你

    最佳答案

    我面临着类似的问题。然而你帮了我。

    我做的不同/会做不同的事情。代替 Create 或 Update,您可以使用 RuleSets,这取决于它将执行不同 RuleSets 的名称,这将允许您在验证它时识别操作:https://fluentvalidation.net/start#rulesets .此时,您不应该注入(inject)任何依赖于运行时结果的东西,例如它是创建还是更新。

    回答您的问题:

    问题 1. 我想我在上面指出了一个错误。否则对我来说看起来很好。不需要创建包装器来对您的验证进行单元测试,您可以像以下示例一样简单地执行此操作:

     [Test]
    public void Should_have_error_when_val_is_zero()
    {
    validator = new TestModelValidator();
    TestModel testRequest = new TestModel();
    //populate with dummy data
    var result = validator.Validate(testRequest);
    Assert.That(result.Errors.Any(o => o.PropertyName== "ParentVal"));
    }

    问题 2:我将只向验证器注入(inject)一个 scopedFactory 并让它自己解决其依赖关系,而不是注入(inject)它需要的所有东西。但是你在 new CompanyValidator(_unitOfWork, databaseOperation) 里面做什么? ?在 Validator 中注入(inject)任何东西对我来说似乎很奇怪,因为它并不是你真正要注入(inject)的东西来解决规则。我不确定你的情况是什么,但否则,正如我所说,我会注入(inject) scopedFactory 或嵌套类来做到这一点。

    问题3:我想我已经回答了那个问题。

    问题 4:我会尝试创建一个通用的依赖注入(inject),或者将一个验证器数组注入(inject)到某种工厂中,该工厂将根据类型进行解析。

    services.AddScoped(typeof(IValidationFactory<>), typeof(ValidationFactory<>));

    这将根据类型解决我需要的验证器。

    希望这是有道理的。

    更新

    因此,在 CreateMethod 内部将 RuleSet 名称传递给 validate 方法,以便他解决它是 Create 还是 Update。关于范围工厂 https://csharp.hotexamples.com/examples/-/IServiceScopeFactory/-/php-iservicescopefactory-class-examples.html

    例如:
    而不是这个:
    ValidationResult 验证结果 = 等待验证.ValidateAsync(user);

    你可以这样做:
    validator.Validate(person, ruleSet: "Create");

    您也可以像这样解决依赖关系并注入(inject)必要的验证器(我正在按请求类型解析,如果需要,您可以使用字符串键):
      services.AddSingleton<IValidator, Validator1>();
    services.AddSingleton<IValidator, Validator2>();
    services.AddSingleton<IValidator, Validator3>();

    services.AddScoped<Func<Type, IValidator>>(serviceProvider => typeKey =>
    {
    if (typeKey == typeof(Validator1))
    {
    return serviceProvider.GetService<Validator1>();
    }
    if (typeKey == typeof(Validator2))
    {
    return serviceProvider.GetService<Validator2>();
    }

    if (typeKey == typeof(Validator3))
    {
    return serviceProvider.GetService<Validator3>();
    }

    return null;
    });

    这是使用示例:
     public GenericValidator(Func<Type, IValidator> validatorFactory)
    {
    _validatorFactory = validatorFactory ?? throw new ArgumentNullException(nameof(validatorFactory));
    }


    public async Task<IEnumerable<string>> ValidateAsync<T, TK>(TK objectToValidate) where TK : class
    {
    var validator = _validatorFactory(typeof(T));

    if (validator == null)
    {
    throw new ValidationException($"Failed to get validator for type: {typeof(T)}");
    }

    var validationResult = await validator.ValidateAsync(objectToValidate);

    return validationResult.Errors.Select(x => x.ErrorMessage);
    }

    并注入(inject): IServiceScopeFactory serviceScopeFactory到您的验证器,这将有助于解决任何外部依赖关系。您可以在此处找到示例: https://csharp.hotexamples.com/examples/-/IServiceScopeFactory/-/php-iservicescopefactory-class-examples.html

    关于c# - 在 .NET Core 中使用 FluentValidation 和依赖注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56202570/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com