Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using FluentValidation in .NET Core with Dependency Injection

I have a .NET Core Web Api Application which is arranged in the following manner -

  1. Controller layer which injects Business Service
  2. Business Service which injects Unit Of Work (to interact with database)
  3. Business Service might also make a call to a FluentValidation class
  4. FluentValidation will inject the Unit Of Work to perform database checks (Exists, etc.)

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.

  1. In your opinion, the way I already have my project structured, is this the best way to use dependency injection with FluentValidation and allow for unit testing of the service method along with unit testing of the FluentValidation class?
  2. Is there a better way using dependency injection with FluentValidation to do all of this, and at the same time let the FluentValidation class know if it is a "Create" or an "Update", instead of creating one class called "UserCreateValidation" and "UserUpdateValidation" or passing in a variable "DatabaseOperation" to the constructor of the Validator?
  3. Appending to (2) when trying to setup the FluentValidation DependencyInjection I am having trouble passing in the "DatabaseOperation" variableservices.AddScoped<IValidator<User>>(x => new UserValidation(x.GetRequiredService<IUnitOfWork>(), <How to figure out if its a create or an update>));
  4. On Top of that I will have to also add two lines to the "Startup.cs" file to create the Scoped DependencyInjection of the "CompanyValidation" and the "RoleValidation" to be used inside of the "UserValidation" and both of those validation classes will also pass in whether or not it is an update or a create.

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

like image 669
BryMan Avatar asked May 18 '19 20:05

BryMan


People also ask

How do you inject FluentValidation?

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.

What is FluentValidation .NET core?

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.

Should I use FluentValidation?

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.

What is FluentValidation C#?

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.

What is fluent validation in ASP NET Core?

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.

How to add dependency injection to a service in NET Core?

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.

How do I enable MVC integration with fluentvalidation?

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:

How do I use ivalidator in dependency injection?

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.


1 Answers

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

like image 180
vsarunov Avatar answered Oct 19 '22 18:10

vsarunov