How to validate two async rules using single api call / How to share async state between multiple validators

Viewed 130

I have multiple async rules that requires the same API calls.

How can I avoid calling the same api twice in a single validation run?

public class ModelValidator : AbstractValidator<Model>
{
    public ReservationDetailModelValidator(IApiClient api)
    {
        RuleFor(x => x.Amount)
           .MustAsync(async (x, val) => 
           {
              var apiResult = api.Fetch(x.Id);
              return apiResult.Value < value)
           }).WithMessage("Validation rule 1");

           .MustAsync(async (x, val) => 
           {
              var apiResult = api.Fetch(x.Id);
              return apiResult.OtherProperty == x.OtherProperty)
           }).WithMessage("Validation rule 2");
    }
}

Should I use RootContextData, or override prevalidate?

1 Answers

A work around for this is using a method that will return the cached value if it exists otherwise fetch the data, cache it and then return it.

public class ModelValidator  : AbstractValidator<Model>
{
    private readonly IApiClient _apiClient;
    private ApiResult? _apiResult;

    public ModelValidator(IApiClient apiClient)
    {
        _apiClient = apiClient;
        
        DefineRules();
    }

    private async Task<ApiResult> GetOrFetchAsync(int id)
    {
        /*
         * Same as :
         *
         * if (_apiResult is null)
         *    _apiResult = await _apiClient.Fetch(id);
         *
         * return _apiResult;
         * 
         */
        return _apiResult ??= await _apiClient.Fetch(id);
    }
    
    private void DefineRules()
    {
        RuleFor(x => x.Amount)
            .MustAsync(async (x, val) => 
            {
                // use of `await GetOrFetchAsync`
            })
            .WithMessage("Validation rule 1")
            
            .MustAsync(async (x, val) =>
            {
                // use of `await GetOrFetchAsync`
            })
            .WithMessage("Validation rule 2");
    }
}
Related