Is it a good practice to inject DbContext or repository into fluent validation validators

Viewed 2247

What do you think about this idea: Some DTOs need validations that involves database interactions, so we inject DbContext or repository class inside the validator class and use it for validation.

    public class UserEditSelfResourceValidator : AbstractValidator<UserEditSelfResource>
    {
        private IUserRepository _userRepository;

        public UserEditSelfResourceValidator(IUserRepository userRepository, ILoggedInUserService loggedInUser)
        {
            _userRepository = userRepository;
            RuleFor(mem => mem.ProfileUrl).MustAsync(async (entity, value, c) => await UniqueProfileUrl(entity, value))
                .WithMessage("Profile Url must be unique.");

        }

        public async Task<bool> UniqueProfileUrl(UserEditSelfResource userEditSelf, string newProfileUrl)
        {
            var user = await _userRepository.FindByProfileUrlAsync(newProfileUrl);
            
            if(user.Id == _loggedInUser.GetId() || user == null)
            {
                return true;
            }
            return false;
        }
    }

Do you consider such a think as a good practice or something is wrong about that?

2 Answers

Short answer Yes, this will ensure that the validation is in one place separated from other logic, but be careful about the lifetime of the objects you insert inside the Validator class, if the Validator is Singleton this will cause an issue, as mentioned here in the document.

'Validators that are registered automatically using RegisterValidatorsFromAssemblyContaining are registered as Scoped with the container rather than as Singleton. This is done to avoid lifecycle scoping issues where a developer may inadvertently cause a singleton-scoped validator from depending on a Transient or Request-scoped service (for example, a DB context). If you are aware of these kinds of issues and understand how to avoid them, then you may choose to register the validators as singletons instead, which will give a performance boost by passing in a second argument:`

I believe this is an answer that is pretty straight forward: From the 10,000 foot perspective, what you are doing looks fine.

The reason I say this, is because you are keeping your validation in one place. Since your DbContext will be injected, you aren't polluting your code with separate and repeated calls to the database every time you need to do this specific validation.

As long as your database calls are just using the bare MINIMUM needed to handle any given specific validation task (you don't want to return the entire object just to select one property) you are keeping your concerns separated. I am assuming you will be injecting your validation service into places like Controllers and what not.

This is just a personal preference, but I think a rule of thumb is, "will this be easy for the next person to understand if he or she needs to pick up where I left off, without any direct explanations?" Keeping your concerns separated (such as the validation service you are trying to implement) means if I need to make a fix or change to validation, I know exactly the place to go to, and I don't have to know much about everything else, such as what is consuming service. Everything I need is right there in that one place.

Related