Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I use this for Async validation? #48

Closed
acnicholls-kroll opened this issue Dec 16, 2024 · 6 comments
Closed

How do I use this for Async validation? #48

acnicholls-kroll opened this issue Dec 16, 2024 · 6 comments
Labels
question Further information is requested

Comments

@acnicholls-kroll
Copy link

Hi,

I've just found your tool and after spending a few minutes looking at the documentation and trying to use the package in my solution, I see that it's not working quite yet.

I have added "AutoValidation" with "All" strategy, but am not seeing the validators hit, and when I do, I'm seeing this error The service provider has not been configured to work with FluentValidation. Making use of InjectValidator or GetServiceProvider is only supported when using the automatic MVC integration.

I thought that using this package would allow for using MustAsync on my RuleFor statements. How do I use this package to enable using IServiceProvider in my validators? I am using the ValidationContext.GetRequiredService method in my validators to find my services, but as you see above, this does not seem to be possible.

@mvdgun
Copy link
Member

mvdgun commented Dec 16, 2024

Hi @acnicholls-kroll, could you share some examples of what you're aiming to achieve? I'm currently using async validation with constructor injection in validators without any issues. I'd be curious to see how you’ve set everything up.

@mvdgun mvdgun added the question Further information is requested label Dec 16, 2024
@acnicholls-kroll
Copy link
Author

acnicholls-kroll commented Dec 16, 2024

I have written IRuleBuilder extensions, which did accept repository classes and performed db checks in an async manner. But the repo layer classes instantiate EFCore DbContext's and when run async like that has concurrent access issues with the DbContext. So, I thought I could use FV's ValidationContext to get a ServiceProvider and make the repo classes transient so the validation can perform multiple db queries concurrently and this would even make validation faster, FV's limitation of a Service Provider only being used with the built-in AspNet validation, that seems impossible.

All my Validators inherit from AbstractValidator<T> and they use the extension methods I wrote to perform the same validations for multiple validators, without duplicating code as this just chains onto the end of a RuleFor(x => x.Guid)

    public static IRuleBuilderOptions<T, Guid> ValidateGroupIdExists<T>(
          this IRuleBuilder<T, Guid> ruleBuilder
        )
    {
      return ruleBuilder.MustAsync(async
        (T command, Guid groupId, ValidationContext<T> context, CancellationToken token) =>
      {
        var repository = context.GetServiceProvider().GetRequiredService<IGroupRepository>();
        var group = await repository.GetByIdAsync(groupId);
        if (group == null)
        {
          context.MessageFormatter.AppendArgument("GroupId", groupId);
          return false;
        }
        return true;
      }).WithMessage("Group with Id '{GroupId}' not found");
    }

In addition, FV's GetServiceProvider does not work in this context, only if the validator is being used as an attribute, The fact that even that usage is being obsoleted just makes this method of validation seem unsuitable for this application, unless I can make it all work in an async manner

The crux of the matter when these IRuleBuilderOptions extensions accept repository methods is when we chain them together on the same property in a validator class that has the repo injected. see below code example:

    public static IRuleBuilderOptions<T, Guid> ValidateGroupIdExists<T>(
          this IRuleBuilder<T, Guid> ruleBuilder,
          IGroupRepository repository
        )
    {
      return ruleBuilder.MustAsync(async
        (T command, Guid groupId, ValidationContext<T> context, CancellationToken token) =>
      {
        var group = await repository.GetByIdAsync(groupId);
        if (group == null)
        {
          context.MessageFormatter.AppendArgument("GroupId", groupId);
          return false;
        }
        return true;
      }).WithMessage("Group with Id '{GroupId}' not found");
    }

the same repository instance goes into each rule method at the same time and EFCore complains about two (or more) concurrent operations. so I'd be more interested to see what you have working, rather than trying to explain what I have that doesn't work....

@mvdgun
Copy link
Member

mvdgun commented Dec 17, 2024

I don't do any injects in extension methods. Normally I create something like a RemoteDataValidator class which I inject into the validators directly, methods on that class I call from either Must or MustAsync.

@acnicholls-kroll
Copy link
Author

does your RemoteDataValidator class take constructor injection or method injection? sounds like constructor injection might be overkill if you don't use all the repository classes.

@mvdgun
Copy link
Member

mvdgun commented Dec 17, 2024

@acnicholls-kroll I am using construction injection. Not sure what you mean with overkill, I prefer construction injection.

@mvdgun
Copy link
Member

mvdgun commented Dec 17, 2024

Closing since this isn't a bug within the library.

@mvdgun mvdgun closed this as completed Dec 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants