在 blazor 中对复杂模型进行自定义远程验证?

Custom remote validations for complex models in blazor?

提问人:Josh 提问时间:1/23/2021 最后编辑:Josh 更新时间:1/24/2021 访问量:1873

问:

我目前正在用于验证复杂模型。 到目前为止一切顺利,除了还需要对数据库进行检查,以查看是否已经存在具有相同值的记录。<ObjectGraphDataAnnotationsValidator/>

我已尝试按照 https://learn.microsoft.com/en-us/aspnet/core/blazor/forms-validation?view=aspnetcore-5.0#validator-components 中的建议实施<CustomValidator/>

但是,它似乎仅适用于顶级属性。

并且不适用于远程验证(或者是吗!?<ObjectGraphDataAnnotationsValidator/>

所以说我有:

*Parent.cs*
public int ID {get;set;}
public List<Child> Children {get;set;}

*Child.cs*
public int ID {get;set;}
public int ParentID {get;set}
public string Code {get;set;}

<EditForm Model="@Parent">
.
.
.

Child.Code在数据库中具有唯一约束。

我想警告用户,这样就不会抛出异常。"This 'Code' already exists! Please try entering a different value."

目前,我对下一步在哪里有点迷茫。

过去,使用 asp.net 核心 mvc,我可以使用远程验证来实现这一点。

是否有等效于 blazor 中的远程验证?

如果没有,我应该怎么做才能获得相同的结果,以远程验证复杂模型的子属性?

任何建议将不胜感激。谢谢!


[根据@rdmptn的建议更新2021/01/24]

ValidationMessageStore.Add()接受结构,这意味着我可以简单地添加一个重载来使其工作:FieldIdentifierCustomValidator.DisplayErrors

        public void DisplayErrors(Dictionary<FieldIdentifier, List<string>> errors)
        {
            foreach (var err in errors)
            {
                messageStore.Add(err.Key, err.Value);
            }

            CurrentEditContext.NotifyValidationStateChanged();
        }

完整示例如下:


@using Microsoft.AspNetCore.Components.Forms
@using System.ComponentModel.DataAnnotations
@using System.Collections.Generic


<EditForm Model="parent" OnSubmit="Submit">
    <ObjectGraphDataAnnotationsValidator></ObjectGraphDataAnnotationsValidator>
    <CustomValidator @ref="customValidator"></CustomValidator>
    <ValidationSummary></ValidationSummary>
    @if (parent.Children != null)
    {
        @foreach (var item in parent.Children)
        {
            <div class="form-group">
                <label>Summary</label>
                <InputText @bind-Value="item.Code" class="form-control"></InputText>
            </div>
        }
    }
    <input type="submit" value="Submit" class="form-control"/>
</EditForm>

@code{
    private CustomValidator customValidator;
    private Parent parent;

    public class Parent
    {
        public int Id { get; set; }
        [ValidateComplexType]
        public List<Child> Children { get; set; }
    }

    public class Child
    {
        public int Id { get; set; }
        public int ParentId { get; set; }
        public string Code { get; set; }
    }

    protected override void OnInitialized()
    {
        parent = new Parent()
        {
            Id = 1,
            Children = new List<Child>()
            {
                new Child()
                {
                    Id = 1,
                    ParentId = 1,
                    Code = "A"
                },
                new Child()
                {
                    Id = 1,
                    ParentId = 1,
                    Code = "B"
                }
            }
        };
    }

    public void Submit()
    {
        customValidator.ClearErrors();

        var errors = new Dictionary<FieldIdentifier, List<string>>();

        //In real operations, set this when you get data from your db
        List<string> existingCodes = new List<string>()
        {
            "A"
        };

        foreach (var child in parent.Children)
        {
            if (existingCodes.Contains(child.Code))
            {
                FieldIdentifier fid = new FieldIdentifier(model: child, fieldName: nameof(Child.Code));
                List<string> msgs = new List<string>() { "This code already exists." };
                errors.Add(fid, msgs);
            }
        }

        if (errors.Count() > 0)
        {
            customValidator.DisplayErrors(errors);
        }
    }
}
blazor-server-side remote-validation

评论


答:

1赞 Just the benno 1/23/2021 #1

验证属性绑定到 MVC,不适用于 Blazor。[Remote]

ObjectGraphDataAnnotationsValidator是不够的。此外,表示具有可能验证的对象的每个属性都需要使用属性进行修饰。[ValidateComplexType]

在 CustomValidatior 中,可以看到 DI 用于获取 API 服务来调用 API 并验证约束。

public class Parent
{
   ...other properties...

   [ValidateComplexType]
   public List<Child> Children {get; set; }
}

public class Child
{
     ...other properties...

    [Required]
    [IsUnique(ErrorMessage = "This 'Code' already exists! Please try entering a different value.")]
    public String Code {get; set;}

}

public class IsUniqueAttribute : ValidationAttribute
{

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var service = (IYourApiService)validationContext.GetService(typeof(IYourApiService));

        //unfortunately, no await is possible inside the validation

        Boolean exists = service.IsUnique((String)value);
        if(exists == false)
        {
            return ValidationResult.Success;
        }

        return new ValidationResult(ErrorMessage, new[] { validationContext.MemberName });
    }
}

您可能需要查看 FluentValidation,因为此库提供了异步验证功能。我不确定此验证器是否可以在 Blazor WASM 中使用。

评论

0赞 rdmptn 1/24/2021
使用本机代码的异步方法可以验证重复项的有效表单提交,这样您就不会一直调用服务器 API。只需处理服务调用中的错误响应以更新模型
1赞 Josh 1/24/2021
嗨,@rdmptn,感谢您的建议!这给了我一个想法,我可以只使用本机 ValidationMessageStore.Add()。我认为这是最好的答案,因为 1.我不必为每个验证 2 创建一个新的自定义属性/数据库服务。无需每次验证时都调用服务器。3.它可以是异步的。我已经更新了我问题的答案。
0赞 enet 1/24/2021
@Wlbjtsthy,我不确定我是否理解 ValidationMessageStore.Add() 如何在这里为您提供帮助......您不必为每次验证创建新的自定义属性/数据库服务,只有在您需要根据数据库中的值验证输入的数据时......“无需每次验证时都调用服务器。怎么会这样?您将在哪里获取数据以根据用户输入的数据进行验证?从 ValidationMessageStore ;}我不确定你是否理解了整个画面......注意:异步编码很重要,但是......意识到您的代码在服务器上执行,而不是在浏览器上执行,因此,
0赞 enet 1/24/2021
您可以将相关数据(例如电话列表)缓存在查找表中,以便对其执行验证。我相信,在这种情况下,性能和异步编码的差异不会很明显。但是,如果您可以异步编码,请这样做,请告诉我们您的情况如何。我也想学习新的技巧。重要提示:AJAX 的想法从一开始就是关于响应能力的。
0赞 enet 1/24/2021
我们被告知,我们的应用程序应该及时响应用户输入的数据。当引入远程验证属性时,我们被告知创建该属性是为了让应用程序能够及时响应用户输入的数据,即使验证涉及查询距离执行验证的用户浏览器数千英里的数据库也是如此......因此,仅在用户尝试保存数据时执行验证是不可接受的