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