0

In my razor file:

<EditForm id="@EditFromId" Model="@Entity" OnValidSubmit="Save">
    <FluentValidator TValidator="PersonalInformationValidator" />

In my PersonalInformationValidator class:

public PersonalInformationValidator(IStringLocalizer<ErrorResource> Loc)
{
    ClassLevelCascadeMode = CascadeMode.Continue;

    RuleFor(entity => entity.FatherName)
        .NotEmpty().WithMessage(x=>Loc["Father-Name-Required"])
        .Length(3, 50).WithMessage(x => Loc["Father-Name-Length"]);
}

I get a compilation time error:

Error CS1662 Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type

and

Error CS0310 'PersonalInformationValidator' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'TValidator' in the generic type or method 'FluentValidator'

Tried to add this line to the Program.cs

builder.Services.AddTransient<IValidator<PersonalInformation>, PersonalInformationValidator>();

Also tried to create a parameterless contractor PersonalInformationValidator. It resolves the compile time error but the validator does not work.

I am using FluentValidation (11.4.0)

and this class on the client side:

public class FluentValidator<TValidator> : ComponentBase where TValidator : IValidator, new()
{
    private readonly static char[] separators = new[] { '.', '[' };
    private TValidator? validator;

    [CascadingParameter] private EditContext? EditContext { get; set; }

    protected override void OnInitialized()
    {
        if (EditContext != null)
        {
            validator = new TValidator();
            var messages = new ValidationMessageStore(EditContext);
            EditContext.OnFieldChanged += (sender, eventArgs)
                => ValidateModel((EditContext?)sender, messages);

            EditContext.OnValidationRequested += (sender, eventArgs)
                => ValidateModel((EditContext?)sender, messages);
        }
    }

    private void ValidateModel(EditContext? editContext, ValidationMessageStore messages)
    {
        if (editContext != null)
        {

            var context = new ValidationContext<object>(editContext.Model);
            var validationResult = validator?.Validate(context);
            messages.Clear();
            if (validationResult != null)
            {
                foreach (var error in validationResult.Errors)
                {
                    var fieldIdentifier = ToFieldIdentifier(editContext, error.PropertyName);
                    messages.Add(fieldIdentifier, error.ErrorMessage);
                }
                editContext.NotifyValidationStateChanged();
            }
        }
    }

    private static FieldIdentifier ToFieldIdentifier(EditContext editContext, string propertyPath)
    {
        var obj = editContext.Model;

        while (true)
        {
            var nextTokenEnd = propertyPath.IndexOfAny(separators);
            if (nextTokenEnd < 0)
            {
                return new FieldIdentifier(obj, propertyPath);
            }

            var nextToken = propertyPath.Substring(0, nextTokenEnd);
            propertyPath = propertyPath.Substring(nextTokenEnd + 1);

            object newObj;
            if (nextToken.EndsWith("]"))
            {

                nextToken = nextToken.Substring(0, nextToken.Length - 1);
                var prop = obj.GetType().GetProperty("Item");
                var indexerType = prop?.GetIndexParameters()[0].ParameterType;
                var indexerValue = Convert.ChangeType(nextToken, indexerType);
                newObj = prop?.GetValue(obj, new object[] { indexerValue });
            }
            else
            {
                var prop = obj.GetType().GetProperty(nextToken);
                if (prop == null)
                {
                    throw new InvalidOperationException($"Could not find property named {nextToken} on object of type {obj.GetType().FullName}.");
                }
                newObj = prop?.GetValue(obj);
            }

            if (newObj == null)
            {
                return new FieldIdentifier(obj, nextToken);
            }

            obj = newObj;
        }
    }
}
1
  • May I know which package / Fluent Validation version that you used? Commented Apr 2, 2023 at 5:47

1 Answer 1

1
  1. Remove the constraint with parameterless constructor new() from the TValidator constraint.

  2. Get the IStringLocalizer<ErrorResource> service from the DI.

  3. With Activator.CreateInstance() create TValidator instance with provide the Loc as parameter.

public class FluentValidator<TValidator> : ComponentBase
    where TValidator : IValidator
{
    private readonly static char[] separators = new[] { '.', '[' };
    private TValidator? validator;

    [CascadingParameter] private EditContext? EditContext { get; set; }

    [Inject]
    IStringLocalizer<ErrorResource> Loc { get; set; }

    protected override void OnInitialized()
    {
        if (EditContext != null)
        {
            validator = (TValidator)Activator.CreateInstance(typeof(TValidator), new object[] { Loc });

            ...
        }
    }

    ...
}

Demo

enter image description here

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.