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;
}
}
}
Remove the constraint with parameterless constructor new()
from the TValidator
constraint.
Get the IStringLocalizer<ErrorResource>
service from the DI.
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