I have a .NET Core Web Api Application which is arranged in the following manner -
So having said all of that here is an example. If I want to create a User in the system I have a route/method called "PostUser" located inside of the "UsersController". The "UsersController" injects the "UserService". The "UserService" has a method called "CreateUser". So inside of the "PostUser" method of the controller it looks like this -
var user = _userService.CreateUser(user);
Now inside of the "CreateUser" method it looks like this -
UserValidation validation = new UserValidation(UnitOfWork, DatabaseOperation.Create);
ValidationResult validationResult = await validation.ValidateAsync(user);
So the UnitOfWork is passed into the UserService via dependency injection and then passed along to the FluentValidation class "UserValidation" so the validation class can perform database checks. I also pass an enum into the UserValidation class to specify whether or not the validation is intended for an Update or a Create.
The User object I am trying to validate will have properties such as "Role" and "Company" and I also have separate validation classes for each (RoleValidation and CompanyValidation). Both of these validation classes will also pass in a UnitOfWork and whether or not this is a create or an update.
Here is an example of my UserValidation Class -
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));
}
}
Now understanding all of this I wanted to create Unit Tests for my "UserService" class. But I believe in order to properly do this I would need to Mock out the FluentValidation class in some cases and as you can see in my "UserService" CreateUser method I instantiate the concrete class for my Validation. So in order to do this I would have to create an interface for each of my fluentvalidation classes and inject them into the business services that use them. So I did the following in my Startup.cs file -
services.AddScoped<IValidator<User>>(x => new UserValidation(x.GetRequiredService<IUnitOfWork>()));
So now after doing that I can inject the IValidator into my UserService Constructor and use that instead of instatiating a Concrete class inside of my UserService methods.
So with this brings me to ask the following questions.
services.AddScoped<IValidator<User>>(x => new UserValidation(x.GetRequiredService<IUnitOfWork>(), <How to figure out if its a create or an update>));
Any help/suggestions would be appreciated. I am really stuck on this issue. If anyone needs anymore clarification on the issues I am facing please do not hesitate to ask.
Thank You
To inject a validator for a specific model, you should register the validator with the service provider as IValidator<T> , where T is the type of object being validated. This validator can be registered as IValidator<User> in your application's startup routine by calling into the . NET service provider.
Fluent Validation is a free to use . NET validation library that helps you make your validations clean, easy to create, and maintain. It even works on external models that you don't have access to, with ease. With this library, you can separate the model classes from the validation logic like it is supposed to be.
Summary. FluentValidation provides a great alternative to Data Annotations in order to validate models. It gives better control of validation rules and makes validation rules easy to read, easy to test, and enable great separation of concerns.
FluentValidation is a .NET library for building strongly-typed validation rules. It Uses a fluent interface and lambda expressions for building validation rules. It helps clean up your domain code and make it more cohesive, as well as giving you a single place to look for validation logic.
There is a library, Fluent Validations that can turn up the validation game to a whole new level, giving you total control. In this article, we will talk about Fluent Validation and it’s implementation in ASP.NET Core Applications.
But in .NET Core, Microsoft has provided an in-built container. We need to add the namespace, i.e., Microsoft.Extension.DependencyInjection. So, in the startup class, inside the ConfigureServices method, we need to add our dependency into the service collection which will dynamically inject whenever and wherever we want in the project.
Note that Minimal APIs that are part of .NET 6 don’t support automatic validation. To enable MVC integration, you’ll need to add a reference to the FluentValidation.AspNetCore assembly in your web project by installing the appropriate NuGet package. From the command line, you can install the package by typing:
Validators can be used with any dependency injection library, such as Microsoft.Extensions.DependencyInjection. To inject a validator for a specific model, you should register the validator with the service provider as IValidator<T>, where T is the type of object being validated.
I am facing a similar issue. However you helped me out.
What I did differently/Would do differently. instead of Create or Update, you can use RuleSets, depending on the name it will execute different RuleSets, this will allow you to identify the operation when you are validating it: https://fluentvalidation.net/start#rulesets. You should not be injecting anything that is dependen on the runtime result at this point such indication if it is create or update.
Answering your questions:
Question 1. I think I pointed one mistake above. Otherwise looks fine to me. It is not needed to create a wrapper to unit test your validation, you can simple do this like in this example:
[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"));
}
Question 2: I would inject just a single scopedFactory to the validator and let it resolve its depedencies himself, instead of injecting everything that it needs. However what are you doing inside new CompanyValidator(_unitOfWork, databaseOperation)
? It seems strange to me to inject anything in Validator since it is not really something you would inject that resolves the rule. I am not sure what is your case for that, but otherwise I would have, as I said, scopedFactory injected or a Nested class to do that.
Question 3: I think I answered that one already.
Question 4: I would try to create a generic dependency injection, or inject an Array of Validators into somekind of factory which would resolve based on type.
services.AddScoped(typeof(IValidationFactory<>), typeof(ValidationFactory<>));
Which would resolve which validator I need based on type.
Hope this makes sense.
UPDATE
So inside the CreateMethod pass the RuleSet name to the validate method for him to solve if it is a Create or Update. About scoped factory https://csharp.hotexamples.com/examples/-/IServiceScopeFactory/-/php-iservicescopefactory-class-examples.html
For example: Instead of this: ValidationResult validationResult = await validation.ValidateAsync(user);
You can do this:
validator.Validate(person, ruleSet: "Create");
As well you can resolve dependencies and inject necessary validator like this for example (I am resolving by request type you can use a string key if needed):
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;
});
And this is usage example:
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);
}
And inject: IServiceScopeFactory serviceScopeFactory
to your validator which will help resolve any external dependencies. You can find examples here: https://csharp.hotexamples.com/examples/-/IServiceScopeFactory/-/php-iservicescopefactory-class-examples.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With