表单验证在使用FluentValidation在.NET Core 8中不验证mudSelect。

huangapple go评论60阅读模式
英文:

form validation does not validate mudSelect when using FluentValidation in .net core 8

问题

我有一个名为"Dance"的类

public class Dance
{
    public int ID { get; set; }
    public string DisplayName { get; set; }
    public DanceType DanceType { get; set; }
    public string Letter { get; set; }
    public Motion Motion { get; set; }
    public int Level { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidUntil { get; set; }
}

其中引用了另外两个类:

public class DanceType
{
    public int ID { get; set; }
    public string DisplayName { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidUntil { get; set; }
}

public class Motion
{
    public int ID { get; set; }
    public string DisplayName { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidUntil { get; set; }
}

我正在尝试使用MudBlazor设置一个表单,以便用户可以添加新的舞蹈,但我无法使验证工作。虽然我甚至还没有尝试验证"DanceType",但"Motion"甚至都没有被验证,我不明白为什么。

这是我的表单:

<MudForm Model="@model" @ref="@form" Validation="@(danceValidator.ValidateValue)" ValidationDelay="0">
    <MudCardContent>
        <MudTextField @bind-Value="model.DisplayName"                              
                      For="@(model.DisplayName)"
                      Immediate="true"
                      Label="Name" />
    </MudCardContent>
    
    <MudCardContent>
        <MudTextField @bind-Value="model.Letter"                              
                      For="@(model.Letter)"
                      Immediate="true"
                      Label="Letter" />
    </MudCardContent>
    
    <MudSelect @bind-Value="model.Motion" Label="Select Motion" Placeholder="Please Select">
        @foreach (Motion item in Motions)
        {
            <MudSelectItem Value="@item">@item.DisplayName</MudSelectItem>
        }
    </MudSelect>

    <MudCardContent>
        <MudTextField @bind-Value="model.Level"                              
                      For="@(model.Level)"
                      Immediate="true"
                      Label="Level" />
    </MudCardContent>

    <MudCardContent>
        <MudDatePicker PickerVariant="PickerVariant.Dialog" Label="Valid From" DateFormat="dd/MM/yyyy" Date="@(new System.DateTime(2020,10,19))" />
    </MudCardContent>
    <MudCardContent>
        <MudDatePicker PickerVariant="PickerVariant.Dialog" Label="Valid Until" DateFormat="dd/MM/yyyy" Date="@(new System.DateTime(2020,10,19))" />
    </MudCardContent>
</MudForm>

和代码:

List<Motion> Motions = new List<Motion>();

MudForm form;

DanceFluentValidator danceValidator = new DanceFluentValidator();
Dance model = new Dance();

protected override async Task OnInitializedAsync()
{
    await GetAllMotions();
}

private async Task GetAllMotions()
{
    Motions = Dal.GetAllMotions();
}

private async Task Submit()
{
    await form.Validate();

    if (form.IsValid)
    {
        var response = Dal.AddDance(model);
        if (response.IsSuccess)
        {
            Snackbar.Add("Submitted!");
            await form.ResetAsync();
        }
        else
        {
            Snackbar.Add("Failed: " + response.ResponseText);
        }
    }
}

public class MotionFluentValidator : AbstractValidator<Motion>
{
    public MotionFluentValidator()
    {
        RuleFor(x => x.DisplayName).NotEmpty().Length(1, 99);
    }
}

public class DanceFluentValidator : AbstractValidator<Dance>
{
    public DanceFluentValidator()
    {
        RuleFor(x => x.DisplayName)
            .NotEmpty()
            .Length(1, 100);
        RuleFor(x => x.Letter)
            .NotEmpty()
            .Length(1, 1);
        RuleFor(x => x.DisplayName)
            .NotEmpty()
            .Length(1, 100);
        RuleFor(x => x.Level)
            .NotEmpty();
        RuleFor(x => x.Motion).NotNull().WithMessage("Please select a motion");
        RuleFor(x => x.ValidFrom).NotNull().WithMessage("Valid From Date is not a valid date.");
        RuleFor(x => x.ValidUntil).NotNull().WithMessage("Valid Until Date is not a valid date.");
    }

    private async Task<bool> IsUniqueAsync(string email)
    {
        // 模拟长时间运行的 HTTP 调用
        await Task.Delay(2000);
        return email.ToLower() != "test@test.com";
    }

    public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
    {
        var result = await ValidateAsync(ValidationContext<Dance>.CreateWithOptions((Dance)model, x => x.IncludeProperties(propertyName)));
        
        if (result.IsValid)
            return Array.Empty<string>();
        return result.Errors.Select(e => e.ErrorMessage);
    };
}

由于某种原因,选择框根本没有被验证。在"ValidateValue"中单步调试时,我无法看到它被检查。我可以看到所有其他属性,但"Motion"根本没有显示。

我还尝试设置第二个验证器并链接这两个:

public class MotionFluentValidator : AbstractValidator<Motion>
{
    public MotionFluentValidator()
    {
        RuleFor(x => x.DisplayName).NotEmpty().Length(1, 99);
    }
    
    public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
    {
        var result = await ValidateAsync(ValidationContext<Motion>.CreateWithOptions((Motion)model, x => x.IncludeProperties(propertyName)));
        
        if (result.IsValid)
            return Array.Empty<string>();
        return result.Errors.Select(e => e.ErrorMessage);
    };
}

public class DanceFluentValidator : AbstractValidator<Dance>
{
    public DanceFluentValidator()
    {
        RuleFor(x => x.DisplayName)
            .NotEmpty()
            .Length(1, 100);
        RuleFor(x => x.Letter)
            .NotEmpty()
            .Length(1, 1);
        RuleFor(x => x.DisplayName)
            .NotEmpty()
            .Length(1, 100);
        RuleFor(x => x.Level)
            .NotEmpty();
        RuleFor(x => x.Motion).SetValidator(new MotionFluentValidator());
        RuleFor(x => x.ValidFrom).NotNull().WithMessage("Valid From Date is not a valid date.");
        RuleFor(x => x.ValidUntil).NotNull().WithMessage("Valid Until Date is not a valid date.");
    }
}

但第二个验证器不会被调用。

此外,将选择下拉框设置为其自己的表

英文:

I have a class called "Dance"

public class Dance
{
    public int ID { get; set; }
    public string DisplayName { get; set; }
    public DanceType DanceType { get; set; }
    public string Letter { get; set; }
    public Motion Motion { get; set; }
    public int Level { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidUntil { get; set; }
}

which references 2 other classes:

public class DanceType
{
    public int ID { get; set; }
    public string DisplayName { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidUntil { get; set; }
}

and

public class Motion
{
    public int ID { get; set; }
    public string DisplayName { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidUntil { get; set; }
}

I am trying to set up a form with MudBlazor so the user can add a new dance, but I cannot get the validation to work. While I havent even tried to validate "DanceType", "Motion" doesnt even get validated and I dont understand why.

here is my form:

&lt;MudForm Model=&quot;@model&quot; @ref=&quot;@form&quot; Validation=&quot;@(danceValidator.ValidateValue)&quot; ValidationDelay=&quot;0&quot;&gt;
	&lt;MudCardContent&gt;
		&lt;MudTextField @bind-Value=&quot;model.DisplayName&quot;                              
		              For=&quot;@(() =&gt; model.DisplayName)&quot;
		              Immediate=&quot;true&quot;
		              Label=&quot;Name&quot; /&gt;
	&lt;/MudCardContent&gt;
	
	&lt;MudCardContent&gt;
		&lt;MudTextField @bind-Value=&quot;model.Letter&quot;                              
		              For=&quot;@(() =&gt; model.Letter)&quot;
		              Immediate=&quot;true&quot;
		              Label=&quot;Letter&quot; /&gt;
	&lt;/MudCardContent&gt;
	
	&lt;MudSelect @bind-Value=&quot;model.Motion&quot;Label=&quot;Select Motion&quot; Placeholder=&quot;Please Select&quot;&gt;
        @foreach (Motion item in Motions)
        {
            &lt;MudSelectItem Value=&quot;@item&quot;&gt;@item.DisplayName&lt;/MudSelectItem&gt;
        }
    &lt;/MudSelect&gt;


	&lt;MudCardContent&gt;
		&lt;MudTextField @bind-Value=&quot;model.Level&quot;                              
		              For=&quot;@(() =&gt; model.Level)&quot;
		              Immediate=&quot;true&quot;
		              Label=&quot;Level&quot; /&gt;
	&lt;/MudCardContent&gt;

	&lt;MudCardContent&gt;
		&lt;MudDatePicker PickerVariant=&quot;PickerVariant.Dialog&quot; Label=&quot;Valid From&quot; DateFormat=&quot;dd/MM/yyyy&quot; Date=&quot;@(new System.DateTime(2020,10,19))&quot; /&gt;
    &lt;/MudCardContent&gt;
	&lt;MudCardContent&gt;
		&lt;MudDatePicker PickerVariant=&quot;PickerVariant.Dialog&quot; Label=&quot;Valid Until&quot; DateFormat=&quot;dd/MM/yyyy&quot; Date=&quot;@(new System.DateTime(2020,10,19))&quot; /&gt;
    &lt;/MudCardContent&gt;
	
&lt;/MudForm&gt;

and the code

List&lt;Motion&gt; Motions = new List&lt;Motion&gt;();

MudForm form;

DanceFluentValidator danceValidator = new DanceFluentValidator();
Dance model = new Dance();


protected override async Task OnInitializedAsync()
{
	await GetAllMotions();
}

 
private async Task GetAllMotions()
{
	Motions = Dal.GetAllMotions();
}


private async Task Submit()
{
    await form.Validate();

    if (form.IsValid)
    {
        var response = Dal.AddDance(model);
        if (response.IsSuccess)
        {
	        Snackbar.Add(&quot;Submitted!&quot;);
	        await form.ResetAsync();
        }
        else
        {
            Snackbar.Add(&quot;Failed: &quot; + response.ResponseText);
        }
    }
}


public class MotionFluentValidator : AbstractValidator&lt;Motion&gt;
{
	public MotionFluentValidator()
	{
		RuleFor(x =&gt; x.DisplayName).NotEmpty().Length(1, 99);
	}
}


public class DanceFluentValidator : AbstractValidator&lt;Dance&gt;
{
    public DanceFluentValidator()
    {
        RuleFor(x =&gt; x.DisplayName)
	        .NotEmpty()
	        .Length(1, 100);
        RuleFor(x =&gt; x.Letter)
	        .NotEmpty()
	        .Length(1, 1);
        RuleFor(x =&gt; x.DisplayName)
	        .NotEmpty()
	        .Length(1, 100);
        RuleFor(x =&gt; x.Level)
	        .NotEmpty();
        RuleFor(x =&gt; x.Motion).NotNull().WithMessage(&quot;Please select a motion&quot;);
        RuleFor(x =&gt; x.ValidFrom).NotNull().WithMessage(&quot;Valid From Date is not a valid date.&quot;);
        RuleFor(x =&gt; x.ValidUntil).NotNull().WithMessage(&quot;Valid Until Date is not a valid date.&quot;);
    }

    private async Task&lt;bool&gt; IsUniqueAsync(string email)
    {
        // Simulates a long running http call
        await Task.Delay(2000);
        return email.ToLower() != &quot;test@test.com&quot;;
    }

    public Func&lt;object, string, Task&lt;IEnumerable&lt;string&gt;&gt;&gt; ValidateValue =&gt; async (model, propertyName) =&gt;
    {
        var result = await ValidateAsync(ValidationContext&lt;Dance&gt;.CreateWithOptions((Dance)model, x =&gt; x.IncludeProperties(propertyName)));
        
        if (result.IsValid)
            return Array.Empty&lt;string&gt;();
        return result.Errors.Select(e =&gt; e.ErrorMessage);
    };
}

Fore some reason, the select is not being validated at all. When stepping through "ValidateValue", I cannot see it being checked. I see all the other properties, but "Motion" doesnt show up at all.

I also tried setting up a 2nd validator and chaining the 2:

public class MotionFluentValidator : AbstractValidator&lt;Motion&gt;
{
	public MotionFluentValidator()
	{
		RuleFor(x =&gt; x.DisplayName).NotEmpty().Length(1, 99);
	}
	
	public Func&lt;object, string, Task&lt;IEnumerable&lt;string&gt;&gt;&gt; ValidateValue =&gt; async (model, propertyName) =&gt;
	{
		var result = await ValidateAsync(ValidationContext&lt;Motion&gt;.CreateWithOptions((Motion)model, x =&gt; x.IncludeProperties(propertyName)));
        
		if (result.IsValid)
			return Array.Empty&lt;string&gt;();
		return result.Errors.Select(e =&gt; e.ErrorMessage);
	};
}

	public class DanceFluentValidator : AbstractValidator&lt;Dance&gt;
{
    public DanceFluentValidator()
    {
        RuleFor(x =&gt; x.DisplayName)
	        .NotEmpty()
	        .Length(1, 100);
        RuleFor(x =&gt; x.Letter)
	        .NotEmpty()
	        .Length(1, 1);
        RuleFor(x =&gt; x.DisplayName)
	        .NotEmpty()
	        .Length(1, 100);
        RuleFor(x =&gt; x.Level)
	        .NotEmpty();
        RuleFor(x =&gt; x.Motion).SetValidator(new MotionFluentValidator());
        RuleFor(x =&gt; x.ValidFrom).NotNull().WithMessage(&quot;Valid From Date is not a valid date.&quot;);
        RuleFor(x =&gt; x.ValidUntil).NotNull().WithMessage(&quot;Valid Until Date is not a valid date.&quot;);
    }

but the 2nd validator doesnt get called.

Also, setting the select dropdown as it's own form with it's own model and own validator also doesnt do anything, the form for the select dropdown always returns "valid".

would anybody be able to see what I am missing?

Thank you!

答案1

得分: 1

你在组件上缺少 For 属性。这需要在使用验证的组件中定义。

<MudSelect @bind-Value="model.Motion"
            Label="Select Motion"
            For="@( () => model.Motion )" 
            Placeholder="Please Select">
    @foreach (Motion item in Motions)
    {
        <MudSelectItem Value="@item">@item.DisplayName</MudSelectItem>
    }
</MudSelect>

同样,对于你的 MudDatePickers 也是如此。

<MudDatePicker PickerVariant="PickerVariant.Dialog"
    Label="Valid From"
    DateFormat="dd/MM/yyyy"
    @bind-Date="@( () => model.ValidFrom )"
    For="@( () => model.ValidFrom )"/>

另外,你还缺少了日期的 @bind 指令。它应该使用可空的 DateTime? 而不是 DateTime 类型。

public class Dance
{
    //...
    public DateTime? ValidFrom { get; set; }
    public DateTime? ValidUntil { get; set; }
}

片段

英文:

You're missing the For property on the components. This needs to be defined in the components where you're using validation.

&lt;MudSelect @bind-Value=&quot;model.Motion&quot;
            Label=&quot;Select Motion&quot;
            For=&quot;@(() =&gt; model.Motion)&quot; 
            Placeholder=&quot;Please Select&quot;&gt;
    @foreach (Motion item in Motions)
    {
        &lt;MudSelectItem Value=&quot;@item&quot;&gt;@item.DisplayName&lt;/MudSelectItem&gt;
    }
&lt;/MudSelect&gt;

Same for your MudDatePickers.

&lt;MudDatePicker PickerVariant=&quot;PickerVariant.Dialog&quot;
    Label=&quot;Valid From&quot;
    DateFormat=&quot;dd/MM/yyyy&quot;
    @bind-Date=&quot;@model.ValidFrom&quot;
    For=&quot;@(()=&gt;model.ValidFrom)&quot;/&gt;

Also you were missing @bind directive for the dates. Which uses a nullable DateTime? not a DateTime type.

    public class Dance
    {
        //...
        public DateTime? ValidFrom { get; set; }
        public DateTime? ValidUntil { get; set; }
    }

Snippet

huangapple
  • 本文由 发表于 2023年6月26日 01:28:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76551650.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定