Bry*_*Man 5 c# dependency-injection fluentvalidation .net-core
我有一个 .NET Core Web Api 应用程序,它按以下方式排列 -
说了这么多,这里只是一个例子。如果我想在系统中创建一个用户,我在“UsersController”中有一个名为“PostUser”的路由/方法。“UsersController”注入“UserService”。“UserService”有一个名为“CreateUser”的方法。所以在控制器的“PostUser”方法内部,它看起来像这样 -
var user = _userService.CreateUser(user);
Run Code Online (Sandbox Code Playgroud)
现在在“CreateUser”方法中它看起来像这样 -
UserValidation validation = new UserValidation(UnitOfWork, DatabaseOperation.Create);
ValidationResult validationResult = await validation.ValidateAsync(user);
Run Code Online (Sandbox Code Playgroud)
因此,UnitOfWork 通过依赖注入传递到 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));
}
}
Run Code Online (Sandbox Code Playgroud)
现在理解了所有这些,我想为我的“UserService”类创建单元测试。但我相信为了正确执行此操作,在某些情况下我需要模拟 FluentValidation 类,正如您在“UserService”CreateUser 方法中看到的那样,我为我的 Validation 实例化了具体类。因此,为了做到这一点,我必须为每个 fluentvalidation 类创建一个接口,并将它们注入到使用它们的业务服务中。所以我在 Startup.cs 文件中做了以下操作 -
services.AddScoped<IValidator<User>>(x => new UserValidation(x.GetRequiredService<IUnitOfWork>()));
Run Code Online (Sandbox Code Playgroud)
所以现在这样做之后,我可以将 IValidator 注入到我的 UserService 构造函数中并使用它而不是在我的 UserService 方法中建立一个 Concrete 类。
因此,这让我提出以下问题。
services.AddScoped<IValidator<User>>(x => new UserValidation(x.GetRequiredService<IUnitOfWork>(), <How to figure out if its a create or an update>));任何帮助/建议将不胜感激。我真的被困在这个问题上。如果有人需要进一步澄清我面临的问题,请随时提出。
谢谢你
我面临着类似的问题。不过你还是帮了我。
我做了什么不同的事/会做不同的事。您可以使用 RuleSets,而不是 Create 或 Update,根据名称,它将执行不同的 RuleSets,这将允许您在验证操作时识别操作: https: //fluidation.net/start#rulesets。此时,您不应该注入任何依赖于运行时结果的内容,例如创建或更新的指示。
回答您的问题:
问题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"));
}
Run Code Online (Sandbox Code Playgroud)
问题 2:我只会向验证器注入一个scopedFactory,并让它自己解决其依赖关系,而不是注入它需要的所有内容。不过你在里面做什么呢new CompanyValidator(_unitOfWork, databaseOperation)?在我看来,在 Validator 中注入任何东西似乎很奇怪,因为它实际上并不是您要注入的东西来解析规则。我不确定你的情况是什么,但否则我会像我说的那样,注入scopedFactory或嵌套类来做到这一点。
问题 3:我想我已经回答了这个问题。
问题 4:我会尝试创建一个通用的依赖注入,或者将验证器数组注入到某种工厂中,该工厂将根据类型进行解析。
services.AddScoped(typeof(IValidationFactory<>), typeof(ValidationFactory<>));
这将根据类型确定我需要哪个验证器。
希望这是有道理的。
更新
所以在CreateMethod内部将RuleSet名称传递给validate方法让他判断是Create还是Update。关于作用域工厂https://csharp.hotexamples.com/examples/-/IServiceScopeFactory/-/php-iservicescopefactory-class-examples.html
例如:而不是这样: ValidationResultvalidationResult=awaitvalidation.ValidateAsync(user);
你可以这样做:
validator.Validate(person, ruleSet: "Create");
Run Code Online (Sandbox Code Playgroud)
您也可以解析依赖项并注入必要的验证器,例如(我通过请求类型解析,如果需要,您可以使用字符串键):
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;
});
Run Code Online (Sandbox Code Playgroud)
这是用法示例:
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);
}
Run Code Online (Sandbox Code Playgroud)
并将:注入IServiceScopeFactory serviceScopeFactory到您的验证器中,这将有助于解决任何外部依赖关系。您可以在这里找到示例:https://csharp.hotexamples.com/examples/-/IServiceScopeFactory/-/php-iservicescopefactory-class-examples.html
| 归档时间: |
|
| 查看次数: |
13246 次 |
| 最近记录: |