我有一个.NET Core Web Api应用程序,该应用程序按以下方式排列-
在这里说了所有这些就是一个例子。如果要在系统中创建用户,则在“ UsersController”内部有一个称为“ PostUser”的路由/方法。 “ UsersController”注入“ UserService”。 “ UserService”具有一种称为“ CreateUser”的方法。因此,在控制器的“ PostUser”方法内部,它看起来像这样-
var user = _userService.CreateUser(user);
现在在“ CreateUser”方法中-
UserValidation validation = new UserValidation(UnitOfWork, DatabaseOperation.Create);
ValidationResult validationResult = await validation.ValidateAsync(user);
因此,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));
}
}
现在了解所有这些,我想为我的“ UserService”类创建单元测试。但是我相信,要正确执行此操作,在某些情况下,我需要模拟FluentValidation类,正如您在“ UserService” CreateUser方法中看到的那样,我为Validation实例化了具体的类。因此,为了做到这一点,我必须为我的每个fluentvalidation类创建一个接口,并将它们注入使用它们的业务服务中。因此,我在Startup.cs文件中做了以下操作-
services.AddScoped<IValidator<User>>(x => new UserValidation(x.GetRequiredService<IUnitOfWork>()));
现在,在完成此操作之后,我可以将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>));
任何帮助/建议将不胜感激。我真的被这个问题困扰。如果有人需要进一步澄清我所面临的问题,请不要犹豫。
谢谢
答案 0 :(得分:0)
我正面临类似的问题。但是你帮助了我。
我做了不同的事情/将会做不同的事情。可以使用RuleSet代替Create或Update,而可以使用RuleSets,具体取决于它将执行不同RuleSets的名称,这将使您可以在验证操作时标识操作:https://fluentvalidation.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"));
}
问题2:我只向验证器注入一个scopedFactory,让它自己解决其缺陷,而不是注入所需的一切。但是,您在new CompanyValidator(_unitOfWork, databaseOperation)
内部正在做什么?在Validator中注入任何内容对我来说似乎很奇怪,因为您注入的并不是解决规则的真正内容。我不确定您的情况如何,但是如我所说,否则我将必须注入scopedFactory或Nested类。
问题3:我想我已经回答了那个。
问题4:我将尝试创建泛型依赖项注入,或将验证者数组注入某种工厂中,该工厂将根据类型进行解析。
services.AddScoped(typeof(IValidationFactory <>),typeof(ValidationFactory <>));
哪个会根据类型确定我需要的验证器。
希望这很有道理。
更新
因此,在CreateMethod中,将RuleSet名称传递给validate方法,以便他解决是Create还是Update的问题。关于范围工厂https://csharp.hotexamples.com/examples/-/IServiceScopeFactory/-/php-iservicescopefactory-class-examples.html
例如: 代替这个: ValidationResult validateResult =等待验证。ValidateAsync(user);
您可以这样做:
validator.Validate(person, ruleSet: "Create");
例如,您也可以解决依赖关系并注入必要的验证器(我正在按请求类型进行解析,如果需要,可以使用字符串键):
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);
}
然后将IServiceScopeFactory serviceScopeFactory
注入到您的验证器中,这将有助于解决任何外部依赖项。您可以在此处找到示例:https://csharp.hotexamples.com/examples/-/IServiceScopeFactory/-/php-iservicescopefactory-class-examples.html