关于.NET Attribute在数据校验中的应用教程

2025-05-29 0 89

前言

Attribute(特性)的概念不在此赘述了,相信有点.NET基础的开发人员都明白,用过Attribute的人也不在少数,毕竟很多框架都提供自定义的属性,类似于Newtonsoft.JSON中JsonProperty、JsonIgnore等

自定义特性

.NET 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关。

创建并使用自定义特性包含四个步骤:

  • 声明自定义特性
  • 构建自定义特性
  • 在目标程序元素上应用自定义特性
  • 通过反射访问特性

声明自定义特性

一个新的自定义特性必须派生自System.Attribute类,例如:

?

1

2

3

4

5

6

7

8

9
public class FieldDescriptionAttribute : Attribute

{

public string Description { get; private set; }

public FieldDescriptionAttribute(string description)

{

Description = description;

}

}

?

1

2

3

4

5
public class UserEntity

{

[FieldDescription("用户名称")]

public string Name { get; set; }

}

该如何拿到我们标注的信息呢?这时候需要使用反射获取

?

1

2

3

4

5

6

7

8

9

10
var type = typeof(UserEntity);

var properties = type.GetProperties();

foreach (var item in properties)

{

if(item.IsDefined(typeof(FieldDescriptionAttribute), true))

{

var attribute = item.GetCustomAttribute(typeof(FieldDescriptionAttribute)) as FieldDescriptionAttribute;

Console.WriteLine(attribute.Description);

}

}

执行结果如下:

关于.NET Attribute在数据校验中的应用教程

从执行结果上看,我们拿到了我们想要的数据,那么这个特性在实际使用过程中,到底有什么用途呢?

Attribute特性妙用

在实际开发过程中,我们的系统总会提供各种各样的对外接口,其中参数的校验是必不可少的一个环节。然而没有特性时,校验的代码是这样的:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18
public class UserEntity

{

/// <summary>

/// 姓名

/// </summary>

[FieldDescription("用户名称")]

public string Name { get; set; }

/// <summary>

/// 年龄

/// </summary>

public int Age { get; set; }

/// <summary>

/// 地址

/// </summary>

public string Address { get; set; }

}

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14
UserEntity user = new UserEntity();

if (string.IsNullOrWhiteSpace(user.Name))

{

throw new Exception("姓名不能为空");

}

if (user.Age <= 0)

{

throw new Exception("年龄不合法");

}

if (string.IsNullOrWhiteSpace(user.Address))

{

throw new Exception("地址不能为空");

}

字段多了之后这种代码就看着非常繁琐,并且看上去不直观。对于这种繁琐又恶心的代码,有什么方法可以优化呢?
使用特性后的验证写法如下:

首先定义一个基础的校验属性,提供基础的校验方法

?

1

2

3

4

5

6

7

8

9

10

11

12

13
public abstract class AbstractCustomAttribute : Attribute

{

/// <summary>

/// 校验后的错误信息

/// </summary>

public string ErrorMessage { get; set; }

/// <summary>

/// 数据校验

/// </summary>

/// <param name="value"></param>

public abstract void Validate(object value);

}

然后可以定义常用的一些对应的校验Attribute,例如RequiredAttribute、StringLengthAttribute

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40
/// <summary>

/// 非空校验

/// </summary>

[AttributeUsage(AttributeTargets.Property)]

public class RequiredAttribute : AbstractCustomAttribute

{

public override void Validate(object value)

{

if (value == null || string.IsNullOrWhiteSpace(value.ToString()))

{

throw new Exception(string.IsNullOrWhiteSpace(ErrorMessage) ? "字段不能为空" : ErrorMessage);

}

}

}

/// <summary>

/// 自定义验证,验证字符长度

/// </summary>

[AttributeUsage(AttributeTargets.Property)]

public class StringLengthAttribute : AbstractCustomAttribute

{

private int _maxLength;

private int _minLength;

public StringLengthAttribute(int minLength, int maxLength)

{

this._maxLength = maxLength;

this._minLength = minLength;

}

public override void Validate(object value)

{

if (value != null && value.ToString().Length >= _minLength && value.ToString().Length <= _maxLength)

{

return;

}

throw new Exception(string.IsNullOrWhiteSpace(ErrorMessage) ? $"字段长度必须在{_minLength}与{_maxLength}之间" : ErrorMessage);

}

}

添加一个用于校验的ValidateExtensions

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70
public static class ValidateExtensions

{

/// <summary>

/// 校验

/// </summary>

/// <typeparam name="T"></typeparam>

/// <returns></returns>

public static void Validate<T>(this T entity) where T : class

{

Type type = entity.GetType();

foreach (var item in type.GetProperties())

{

//需要对Property的字段类型做区分处理针对Object List 数组需要做区分处理

if (item.PropertyType.IsPrimitive || item.PropertyType.IsEnum || item.PropertyType.IsValueType || item.PropertyType == typeof(string))

{

//如果是基元类型、枚举类型、值类型或者字符串 直接进行校验

CheckProperty(entity, item);

}

else

{

//如果是引用类型

var value = item.GetValue(entity, null);

CheckProperty(entity, item);

if (value != null)

{

if ((item.PropertyType.IsGenericType && Array.Exists(item.PropertyType.GetInterfaces(), t => t.GetGenericTypeDefinition() == typeof(IList<>))) || item.PropertyType.IsArray)

{

//判断IEnumerable

var enumeratorMI = item.PropertyType.GetMethod("GetEnumerator");

var enumerator = enumeratorMI.Invoke(value, null);

var moveNextMI = enumerator.GetType().GetMethod("MoveNext");

var currentMI = enumerator.GetType().GetProperty("Current");

int index = 0;

while (Convert.ToBoolean(moveNextMI.Invoke(enumerator, null)))

{

var currentElement = currentMI.GetValue(enumerator, null);

if (currentElement != null)

{

currentElement.Validate();

}

index++;

}

}

else

{

value.Validate();

}

}

}

}

}

private static void CheckProperty(object entity, PropertyInfo property)

{

if (property.IsDefined(typeof(AbstractCustomAttribute), true))//此处是重点

{

//此处是重点

foreach (AbstractCustomAttribute attribute in property.GetCustomAttributes(typeof(AbstractCustomAttribute), true))

{

if (attribute == null)

{

throw new Exception("AbstractCustomAttribute not instantiate");

}

attribute.Validate(property.GetValue(entity, null));

}

}

}

}

新的实体类

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22
public class UserEntity

{

/// <summary>

/// 姓名

/// </summary>

[Required]

public string Name { get; set; }

/// <summary>

/// 年龄

/// </summary>

public int Age { get; set; }

/// <summary>

/// 地址

/// </summary>

[Required]

public string Address { get; set; }

[StringLength(11, 11)]

public string PhoneNum { get; set; }

}

调用方式

?

1

2
UserEntity user = new UserEntity();

user.Validate();

上面的校验逻辑写的比较复杂,主要是考虑到对象中包含复杂对象的情况,如果都是简单对象,可以不用考虑,只需针对单个属性做字段校验

现有的方式是在校验不通过的时候抛出异常,此处大家也可以自定义异常来表示校验的问题,也可以返回自定义的校验结果实体来记录当前是哪个字段出的问题,留待大家自己实现

如果您有更好的建议和想法欢迎提出,共同进步

总结

到此这篇关于.NET Attribute数据校验中的应用的文章就介绍到这了,更多相关.NET Attribute数据校验的应用内容请搜索快网idc以前的文章或继续浏览下面的相关文章希望大家以后多多支持快网idc!

原文链接:https://www.cnblogs.com/hexu0512/p/12879671.html

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 关于.NET Attribute在数据校验中的应用教程 https://www.kuaiidc.com/97059.html

相关文章

发表评论
暂无评论