-
-
Notifications
You must be signed in to change notification settings - Fork 87
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
[Bug] Nested Complex Type Validation Fails #76
Comments
Hi @AmirMahdiNassiri, this issue is that you're newing up and passing in the Validator. If you update the <Blazored.FluentValidation.FluentValidationValidator /> Removing the Validator parameter, everything works as expected. What I'm not sure about is where is the problem is. I'm not sure why when Blazored FluentValidation finds your validator it works but when you create it manually it doesn't. Also, the exception is being thrown by FluentValidation itself so that might be something to think about. I'm not sure what to suggest at the moment. |
@chrissainty Thanks for the workaround Chris 👍 I tested and realized that if we pass in the nested type to the validator to validate, the exception is thrown by Have a look at this line in the project. This line passes the nested type ( |
Yep, I see what you're saying. That does seem to be what's causing the issue. That line was based on similar code Microsoft use in the DataAnnotationValidator component. I'm just thinking if there would be any breaking effects of making that change. |
I came across this issue as well and fixed the However, I came across another issue which I'm not sure is related. I'm trying to pass in Foo.razor<EditForm EditContext="EditContext" OnSubmit="() => Submit()">
<FluentValidationValidator />
</EditForm> Foo.razor.cspublic partial class Foo
{
public FooForm Form { get; }
public FooForm.Validator Validator { get; }
public EditContext EditContext { get; }
public Foo()
{
Form = new();
Validator = new FooForm.Validator(DateTime.Today);
EditContext = new EditContext(Form);
}
public void Submit()
{
var isFormValid = EditContext.Validate();
Console.WriteLine(isFormValid);
}
} FooForm.cspublic class FooForm
{
public List<Contact>? Contacts { get; set; }
public class Validator : AbstractValidator<FooForm>
{
public Validator(DateTime today)
{
Console.WriteLine(today.ToString());
RuleForEach(form => form.Contacts).SetValidator(new ContactValidator());
}
}
public class ContactValidator : AbstractValidator<Contact>
{
public ContactValidator()
{
RuleFor(contact => contact.PhoneNumber).NotEmpty().WithMessage("Requried");
}
}
}
public class Contact
{
public string PhoneNumber { get; set; } = "";
} When I submit the form without entering anything in
If this is unrelated and a separate issue, let me know and I'll move it. |
How I'm reading the code is this: If you leave the Validator property null, Then the code asks service provider for a validator when it needs one in the case of fields, it determines what kind of validator it needs based on what type of object the bound property belongs to. So the property belongs to an address, we ask the service provider for an AbstractValidator and get one, even though to entire form is for a Customer object.When we provide an AbstractValidator, the code doesn't ask the ServiceProvider for a validator, but the property that triggered the event still belongs to an object of type address, so the passed in AbstractValidator throws an exception when it's asked to validate an Address I'm not sure I've got it exactly right, but it does seem to explain the issue. I've seen something similar when an input was bound to a component property, which redirected to a property on another object. The validator for the object was provided, but when the field changed, it was the component which was passed into the validator, not the object which actually held the form data. |
Hi @ryanbuening, I can see a couple of issues with your code design, one of which would remove the problem.
public Foo()
{
Form = new();
Validator = new FooForm.Validator(DateTime.Today);
EditContext = new EditContext(Form);
} Would be: protected override void OnInitialized()
{
Form = new();
Validator = new FooForm.Validator(DateTime.Today);
EditContext = new EditContext(Form);
}
public Validator()
{
Console.WriteLine(DateTime.Today.ToString());
RuleForEach(form => form.Contacts).SetValidator(new ContactValidator());
} |
Good to know! I wasn't able to find any docs on this. Is this outlined somewhere? I made the change of putting the instantiation logic in
I believe passing |
The constructor was used in order to satisfy the compilers nullability checks. otherwise it's public EditContext { get; set; } = null!; // I've said it can't be null, but here I am setting it to null to make the compiler happy.
// and now it's possible to throw a null reference exception so I might as well turn nullable references off... But we can look into other ways to solve this and avoid using the constructor. DateTime.Today is an Impure call, so to keep the validator pure we provide the current date as a parameter. In this case, it's a really minor risk that a unit test could fail at some point in the future even though nothing changes (a test which makes sure Jan 1 2022 is in the future, ran on jan 2 2022), but it's more about keeping validators and other business logic consistently pure. |
…wn by FluentValidation Thanks to @AmirMahdiNassiri for pointing to the line in the source code with the issue. Fixes issue Blazored#76
Hi. I created PR #91 to resolve this issue. Thank you. |
@icnocop thanks! Works perfectly. You've made my day so much better. |
issue is back in v2.1.0 |
…wn by FluentValidation Fixes Blazored#76
Describe the bug
Although
FluentValidation
supports nested complex types in the model classes (see here), theFluentValidationValidator
throws an exception when editing the nested complex type:System.InvalidOperationException: Cannot validate instances of type 'Address'. This validator can only validate instances of type 'Customer'
Originating from:
Blazored.FluentValidation.EditContextFluentValidationExtensions.ValidateField(EditContext editContext, ValidationMessageStore messages, FieldIdentifier fieldIdentifier, IServiceProvider serviceProvider, Boolean disableAssemblyScanning, IValidator validator)
To Reproduce
Please have a look at the minimal source project I created to reproduce the issue. Please download the repo, build, and execute the sample.
Expected behavior
Nested complex types should be editable and the validation rules should work accordingly.
Hosting Model (is this issue happening with a certain hosting model?):
Additional context
I guess if the
EditContext.Model
is passed to theValidationContext
instead offieldIdentifier.Model
, validation should execute successfully. Perhaps such a change ineditContext.OnFieldChanged
can fix the problem.Many thanks for the awesome library 👍
The text was updated successfully, but these errors were encountered: