Search code examples
c#asp.net-core-webapifluentvalidation

ASP.NET Core - How to resolve no accessible extension method 'MessageFormatter' Error in Fluent Validation


In ASP.NET Core-8 Web API, I am implementing Custom Messages with Fluent Validation

DTO:

public class CreateAccountRequestDto
{
    public int PreferredClassCode { get; set; }
    public DateTime DateOfBirth { get; set; }
}

CreateAccountRequestDtoValidator:

public class CreateAccountRequestDtoValidator : AbstractValidator<CreateAccountRequestDto>
{
    private readonly IDbDataAccess _dbDataAccess;
    public CreateAccountRequestDtoValidator(
        IDbDataAccess dbDataAccess
        ) 
    {
        _dbDataAccess = dbDataAccess;
    }
    public CreateAccountRequestDtoValidator()
    {
        RuleFor(x => x.DateOfBirth)
            .MustAsync(async (request, dob, context) => await ValidateDateOfBirth(request, dob, context))
            .WithMessage(context => context.MessageFormatter.GetArgument("Message").ToString());
    }

    private async Task<bool> ValidateDateOfBirth(CreateAccountRequestDto request, DateTime dateOfBirth, ValidationContext<CreateAccountRequestDto> context)
    {
        int age = DateTime.Today.Year - dateOfBirth.Year;
        bool isValid = true;
        string message = "";

        if (string.IsNullOrWhiteSpace(request.Bvn) && age < 16)
        {
            message = "Customer must be at least 16 years old without BVN.";
            isValid = false;
        }
        else
        {
            if (request.PreferredClassCode == 999 || request.PreferredClassCode == 888) // Timeless class
            {
                if (age < 55)
                {
                    message = "Customer must be at least 55 years old for this class.";
                    isValid = false;
                }
            }
            else if (request.PreferredClassCode == 357)
            {
                if (age < 16 || age > 25)
                {
                    message = "Customer must be between 16 and 25 for this class.";
                    isValid = false;
                }
            }
        }
        if (!isValid)
        {
            context.MessageFormatter.AppendArgument("Message", message);
        }

        return await Task.FromResult(isValid); 
    }
}

I got these errors:

Severity Code Description Project File Line Suppression State Error CS1503 Argument 3: cannot convert from 'System.Threading.CancellationToken' to 'FluentValidation.ValidationContext<FlexcubeAcopAccountService.DTOs.Request.CreateAccountRequestDto>'

and

Severity Code Description Project File Line Suppression State Error CS1061 'CreateAccountRequestDto' does not contain a definition for 'MessageFormatter' and no accessible extension method 'MessageFormatter' accepting a first argument of type 'CreateAccountRequestDto' could be found (are you missing a using directive or an assembly reference?)

It highlights:

context in await ValidateDateOfBirth(request, dob, context))

and also

MessageFormatter

in

WithMessage(context => context.MessageFormatter.GetArgument("Message").ToString())

I also used this:

        RuleFor(x => x.DateOfBirth)
            .MustAsync(async (request, dob, context) =>
            {
                return await ValidateDateOfBirth(request, dob, context);
            })
            .WithMessage(context => context.MessageFormatter.GetArgument("Message").ToString());

But still got the same error

Kindly help resolve the error.


Solution

  • In your MustAsync(), you have to use the function with 4 arguments to obtain the ValidationContext:

    public static IRuleBuilderOptions<T, TProperty> MustAsync<T, TProperty>(this IRuleBuilder<T, TProperty> ruleBuilder, Func<T, TProperty, ValidationContext<T>, CancellationToken, Task<bool>> predicate)
    

    While retrieving the customized Message from the ValidateDateOfBirth method, you just need to apply the "{Message}" placeholder. (Reference: Custom message placeholders)

    public CreateAccountRequestDtoValidator()
    {
        RuleFor(x => x.DateOfBirth)
            .MustAsync(async (request, dob, context, cancellationToken) => await ValidateDateOfBirth(request, dob, context))
            .WithMessage("{Message}");
    }