【问题标题】:Custom remote validations for complex models in blazor?blazor 中复杂模型的自定义远程验证?
【发布时间】:2021-04-27 15:50:37
【问题描述】:

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

我已尝试按照https://docs.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 core mvc,我可以使用远程验证来实现这一点。

在 blazor 中是否有等同于远程验证的功能?

如果不是,我应该怎么做才能达到相同的结果,远程验证复杂模型的子属性?

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


[在@rdmptn 的建议后更新 2021/01/24]

ValidationMessageStore.Add() 接受结构体FieldIdentifier,这意味着我可以简单地添加CustomValidator.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);
        }
    }
}

【问题讨论】:

    标签: validation blazor blazor-server-side remote-validation


    【解决方案1】:

    [Remote] 验证属性与 MVC 相关联,不适用于 Blazor。

    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,因为该库为asynchronous validation 提供了功能。我不确定这个验证器是否可以在 Blazor WASM 中使用。

    【讨论】:

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