前言
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); } } |
執行結果如下:
從執行結果上看,我們拿到了我們想要的數據,那么這個特性在實際使用過程中,到底有什么用途呢?
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在數據校驗的應用內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://www.cnblogs.com/hexu0512/p/12879671.html