.NET 8 的新增功能-数据验证

.NET
236
0
0
2024-01-19

1.概要

在.NET8中C#的新增特性,System.ComponentModel.DataAnnotations 命名空间包括用于云原生服务中的验证场景的新数据验证特性。 虽然预先存在的 DataAnnotations 验证程序适用于典型的 UI 数据输入验证(例如窗体上的字段),但新特性旨在验证非用户输入数据,例如配置选项。 除了新特性之外,还向 RangeAttribute 和 RequiredAttribute 类型添加了新属性。

新的 API

说明

RangeAttribute.MinimumIsExclusive RangeAttribute.MaximumIsExclusive

指定边界是否包含在允许的范围内。

System.ComponentModel.DataAnnotations.LengthAttribute

指定字符串或集合的下界和上界。 例如,[Length(10, 20)] 要求集合中至少有 10 个元素,最多有 20 个元素。

System.ComponentModel.DataAnnotations.Base64StringAttribute

验证字符串是有效的 Base64 表示形式。

System.ComponentModel.DataAnnotations.AllowedValuesAttribute System.ComponentModel.DataAnnotations.DeniedValuesAttribute

分别指定允许列表和拒绝列表。 例如,[AllowedValues("apple", "banana", "mango")]。

这里我将使用WPF应用作为示例程序进行演示,项目目录结构如下采用的是传统的MVVM模式。接下来我们通过简单的代码示例来了解一下这些新特性的作用。

2.详细内容

将本次更新所有的新特性添加到Model字段中,这里依旧用EmployeeModel来作为示例。如果刚刚接触新的特性不知道如何使用,最简单有效的方式就是F12跟进去看看代码结构就知道如何使用了,我来以AllowedValues举例说明一下:

  • AllowedValuesAttribute(params object?[] values)中初始化可接受数据时会内部维护一个object数组。
  • 在IsValid(object? value)进行遍历校验,如果有不匹配的异常则将DefaultErrorMessage抛出。

这样我们就能很快的知道这些内部是怎么设计的,这样我们使用起来才会更清楚。

namespace System.ComponentModel.DataAnnotations
{
    /// <summary>
    ///     Specifies a list of values that should be allowed in a property.
    /// </summary>
    [CLSCompliant(false)]
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
        AllowMultiple = false)]
    public class AllowedValuesAttribute : ValidationAttribute
    {
        /// <summary>
        ///     Initializes a new instance of the <see cref="AllowedValuesAttribute"/> class.
        /// </summary>
        /// <param name="values">
        ///     A list of values that the validated value should be equal to.
        /// </param>
        public AllowedValuesAttribute(params object?[] values)
        {
            ArgumentNullException.ThrowIfNull(values);
            Values = values;
            DefaultErrorMessage = SR.AllowedValuesAttribute_Invalid;
        }

        /// <summary>
        ///     Gets the list of values allowed by this attribute.
        /// </summary>
        public object?[] Values { get; }

        /// <summary>
        ///     Determines whether a specified object is valid. (Overrides <see cref="ValidationAttribute.IsValid(object)" />)
        /// </summary>
        /// <param name="value">The object to validate.</param>
        /// <returns>
        ///     <see langword="true" /> if any of the <see cref="Values"/> are equal to <paramref name="value"/>,
        ///     otherwise <see langword="false" />
        /// </returns>
        /// <remarks>
        ///     This method can return <see langword="true"/> if the <paramref name="value" /> is <see langword="null"/>,
        ///     provided that <see langword="null"/> is also specified in one of the <see cref="Values"/>.
        /// </remarks>
        public override bool IsValid(object? value)
        {
            foreach (object? allowed in Values)
            {
                if (allowed is null ? value is null : allowed.Equals(value))
                {
                    return true;
                }
            }

            return false;
        }
    }
}

我们再来看看这次demo中的主要代码:

//...using other.
using System.ComponentModel.DataAnnotations;

namespace WpfDataAnnotations
{
    public class EmployeeModel
    {
        /// <summary>
        /// 员工id,Attribute含义:限制id的范围0~int最大值如果不在这个范围内则抛出异常
        /// </summary>
        [Range(0,int.MaxValue)]
        public int Id { get; set; }

        /// <summary>
        /// 员工名称,Attribute含义:限制Name不可以为president(总统),否则抛出ErrorMessage的内容。
        /// </summary>
        [DeniedValues("president")]
        public string Name { get; set; }

        /// <summary>
        /// 年龄 Attribute含义:限制Age的范围0~150岁最大值如果不在这个范围内则抛出异常
        /// </summary>
        [Range(0, 150)]
        public int Age { get; set; }

        //Required Attribute含义:Email字段不能为空(或验证失败),如果为空则抛出ErrorMessage的内容。
        [Required(AllowEmptyStrings = false,ErrorMessage ="邮件字段内容异常请检查!")]
        public string Email { get; set; }

        /// <summary>
        /// 部门,Attribute含义:假设现在只有两个部门,设置值的时候只允许这两个值的出现。否则抛出ErrorMessage的内容。
        /// </summary>
        [AllowedValues("HR", "Develop")]
        public string Department { get; set; }

        /// <summary>
        /// 下属id,Attribute含义:每个员工都有可能有下属,下属最少是1人最多是100人。否则抛出ErrorMessage的内容。
        /// </summary>
        [Length(1, 10)]
        public int[] Underlings { get; set; }

        //Attribute含义:token必须以base64的形式表达。否则抛出ErrorMessage的内容。
        [Base64String]
        public string Token { get; set; }
    }
}
  • 在MainViewModel我们在修改数据时用ValidationContext对象验证一下,看看我们添加的这些新特性有没有生效。
namespace WpfDataAnnotations
{
    public class MainViewModel : BaseViewModel
    {
        private EmployeeModel employeeModel;
        private Command changedCommand;

        public EmployeeModel EmployeeModel 
        { 
            get => employeeModel;
            set 
            {
                employeeModel = value;
                OnPropertyChanged(nameof(EmployeeModel));
            }
        }

        public Command ChangedCommand { get => changedCommand ?? (changedCommand = new Command(ChangedAction)); }

        private void ChangedAction(object obj)
        {
            ValidationContext context = new ValidationContext(EmployeeModel, null, null);

            List<ValidationResult> validationResults = new List<ValidationResult>();

            bool valid = Validator.TryValidateObject(EmployeeModel, context, validationResults, true);

            if (!valid)
            {
                Application.Current.Dispatcher.Invoke(() => 
                {
                    foreach (ValidationResult validationResult in validationResults)
                    {
                        Debug.WriteLine(validationResult.ErrorMessage);
                        //MessageBox.Show(validationResult.ErrorMessage);
                    }
                });
            }
        }

        public MainViewModel() 
        {
            EmployeeModel = new EmployeeModel 
            {
                Id = -1,
                Name = "president",
                Age = 200,
                Email = "",
                Department = "xxx",
                Underlings = new int[0] ,
                Token = "xxxx"
            };
        }
    }
}

运行效果

ref

https://learn.microsoft.com/zh-cn/dotnet/core/whats-new/dotnet-8#data-validation