Blazored / FluentValidation

A library for using FluentValidation with Blazor
https://blazored.github.io/FluentValidation/
MIT License
577 stars 83 forks source link

Is It possible to inject IValidator<TEntity> and get all customstate of object? #159

Open nagtilaklaxman opened 1 year ago

nagtilaklaxman commented 1 year ago

Is It possible to inject IValidator and get all custom state of object?

Sharaf-Mansour commented 1 year ago

Do you want to do manual validations of objects? or do you want to know if an object was validated or not? Can you explain more of what you are trying to achieve with this?

nagtilaklaxman commented 1 year ago

I would like to do manual validation and need to have custom state in object having collection of other objects.

On Sat, Oct 1, 2022, 09:41 Sharaf M. Mansour @.***> wrote:

Do you want to do manual validations of objects? or do you want to know if an object was validated or not? Can you explain more of what you are trying to achieve with this?

— Reply to this email directly, view it on GitHub https://github.com/Blazored/FluentValidation/issues/159#issuecomment-1264131687, or unsubscribe https://github.com/notifications/unsubscribe-auth/AD56CNX6QV3S5NICPPUWWMLWA53DBANCNFSM6AAAAAAQZVKNUU . You are receiving this because you authored the thread.Message ID: @.***>

Sharaf-Mansour commented 1 year ago

@nagtilaklaxman I think I know how to do such thing. Let me do a tiny repo and show a sample to you, then you tell me if that is what you want or not. Give me like few hours :D

Sharaf-Mansour commented 1 year ago

@nagtilaklaxman I did a little test here is what I got so far

using FluentValidation;
using FluentValidation.Results;

namespace StateAndValidation;
public class Person
{
    PersonValidator PersonValidator => new();
    public string? Name { get; set; }
    public int? Age { get; set; }
    public string? EmailAddress { get; set; }
    public List<ValidationFailure> States => PersonValidator.Validate(this).Errors;
    public bool IsValid => PersonValidator.Validate(this).IsValid;

}
public class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {
        RuleFor(x => x.Name).NotEmpty().WithMessage("Name is required");

        RuleFor(x => x.Name).MaximumLength(50).WithMessage( "Name must be less than 50 characters");

        RuleFor(x => x.Name).MinimumLength(5).WithMessage( "Name must be at least 5 characters");

        RuleFor(x => x.Age).NotEmpty().WithMessage( "Age is required");

        RuleFor(x => x.Age).InclusiveBetween(18, 65).WithMessage( "Age must be between 18 and 65");

        RuleFor(x => x.EmailAddress).NotEmpty().WithMessage( "Email address is required");

        RuleFor(x => x.EmailAddress).EmailAddress().WithMessage( "Email address is invalid");

    }
}

Note here:

    public List<ValidationFailure> States => PersonValidator.Validate(this).Errors;
    public bool IsValid => PersonValidator.Validate(this).IsValid;

State will have a list of all the Errors just like the ValidationSummary And IsValid will manually validate and check the state of is valid You can then map the States with enums like this

public enum State
{
    NameShort, NameLong, AgeInvalid, EmailInvalid, NameNull, EmailNull, AgeNull  
}

You can have a Custom State and Validation Components I hope I understood what you need Here is the index.Razor

@page "/"
<EditForm Model="@_person" OnSubmit="@SubmitForm">
    <FluentValidationValidator />
    <ValidationSummary />

    <p>
        <label>Name: </label>
        <InputText @bind-Value="@_person.Name" />
    </p>

    <p>
        <label>Age: </label>
        <InputNumber @bind-Value="@_person.Age" />
    </p>

    <p>
        <label>Email Address: </label>
        <InputText @bind-Value="@_person.EmailAddress" />
    </p>

    <button type="submit">Save</button>
</EditForm>
@if(_person.States is not null){
    <ul>
        @foreach (var state in _person.States)
        {
            <li>@state.ErrorMessage</li>
        }
    </ul>
}
@code {
    private Person _person = new();

    private void SubmitForm()
        => Console.WriteLine(_person.IsValid);

}
nagtilaklaxman commented 1 year ago

Hello, Thanks for your help but this is not i wanted. Fluent validation has custom state property. I dont have EditForm on page it is just preview page of bulk upload via excel and need to validate all records via fluent validation by manually injecting IValidator of model and call validateAsync to get validation result. You can refer

https://docs.fluentvalidation.net/en/latest/custom-state.html

On Sun, Oct 2, 2022, 05:51 Sharaf M. Mansour @.***> wrote:

@nagtilaklaxman https://github.com/nagtilaklaxman I did a little test here is what I got so far

using FluentValidation;using FluentValidation.Results; namespace StateAndValidation;public class Person { PersonValidator PersonValidator => new(); public string? Name { get; set; } public int? Age { get; set; } public string? EmailAddress { get; set; } public List States => PersonValidator.Validate(this).Errors; public bool IsValid => PersonValidator.Validate(this).IsValid;

}public class PersonValidator : AbstractValidator { public PersonValidator() { RuleFor(x => x.Name).NotEmpty().WithMessage("Name is required");

    RuleFor(x => x.Name).MaximumLength(50).WithMessage( "Name must be less than 50 characters");

    RuleFor(x => x.Name).MinimumLength(5).WithMessage( "Name must be at least 5 characters");

    RuleFor(x => x.Age).NotEmpty().WithMessage( "Age is required");

    RuleFor(x => x.Age).InclusiveBetween(18, 65).WithMessage( "Age must be between 18 and 65");

    RuleFor(x => x.EmailAddress).NotEmpty().WithMessage( "Email address is required");

    RuleFor(x => x.EmailAddress).EmailAddress().WithMessage( "Email address is invalid");

}

}

Note here:

public List<ValidationFailure> States => PersonValidator.Validate(this).Errors;
public bool IsValid => PersonValidator.Validate(this).IsValid;

State will have a list of all the Errors just like the ValidationSummary And IsValid will manually validate and check the state of is valid You can then map the States with enums like this

public enum State { NameShort, NameLong, AgeInvalid, EmailInvalid, NameNull, EmailNull, AgeNull }

You can have a Custom State and Validation Components I hope I understood what you need Here is the index.Razor

@page "/" <EditForm Model="@_person" @.***">

<ValidationSummary />

<p>
    <label>Name: </label>
    <InputText @bind-Value="@_person.Name" />
</p>

<p>
    <label>Age: </label>
    <InputNumber @bind-Value="@_person.Age" />
</p>

<p>
    <label>Email Address: </label>
    <InputText @bind-Value="@_person.EmailAddress" />
</p>

<button type="submit">Save</button>

@.***(_person.States is not null){

    @foreach (var state in _person.States) { ***@***.*** }

@.*** { private Person _person = new();

private void SubmitForm()
    => Console.WriteLine(_person.IsValid);
    }

— Reply to this email directly, view it on GitHub https://github.com/Blazored/FluentValidation/issues/159#issuecomment-1264450756, or unsubscribe https://github.com/notifications/unsubscribe-auth/AD56CNWA6WAGIIF7QAZA543WBCB2DANCNFSM6AAAAAAQZVKNUU . You are receiving this because you were mentioned.Message ID: @.***>

Sharaf-Mansour commented 1 year ago

@nagtilaklaxman you can do that in your model.


public async ValueTask<bool> IsValidAsync() => (await PersonValidator.ValidateAsync()).IsValid;

You can use that to trigger the manual validation just inject your model instead and call this function in your submission function (upload button) And this will trigger a manual validation.

nagtilaklaxman commented 1 year ago

How I can access custom state and error messages set via fluent validator? Is It not possible to injectIvalidator<PersonValidator>?

Sharaf-Mansour commented 1 year ago

If you mean adding an interface to the DI container that would require a class that implements Ivalidator<PersonValidator> But here is my question. Why do you want to inject an interface when your AbstractValidator can do the same? You already have the class Called PersonValidator which already implements the ivalidator interface here

public abstract class AbstractValidator<T> : IValidator<T>, IEnumerable<IValidationRule> 

You can inject and use that class as much as you want to do any custom validation or track the state. It is already there!