用過asp.net mvc 的都應該知道,在實體類上添加一些特性,可以實現后端實體的數據校驗,這里簡單實現一下
實現原理:利用反射獲取實體的每一個屬性,並通過屬性獲取屬性上標注的特性,調用特性的Validate方法(此方法自定義的)來驗證屬性的值是否合法。
1、創建自己的校驗特性基類
此類繼承了Attribute,表明為一個特性,Validate方法為抽象方法,目的是給實現的子類自己定義自己的Validate方法。error為錯誤消息提示信息。
1 [AttributeUsage(AttributeTargets.Property,AllowMultiple = true)] 2 public abstract class BaseAttribute:Attribute 3 { 4 public virtual string error { get; set; } 5 public abstract bool Validate(object value); 6 }
2、創建特性類繼承自BaseAttribute
這里只簡單寫3個特性,寫法都一樣,只是校驗方法Validate中的邏輯不一樣。
1 /// <summary> 2 /// 約束屬性不能為空 3 /// </summary> 4 public class RequiredAttribute : BaseAttribute 5 { 6 public override string error { 7 get 8 { 9 if (base.error != null) 10 { 11 return base.error; 12 } 13 return "屬性不能為空"; 14 } 15 set => base.error = value; 16 } 17 public override bool Validate(object value) 18 { 19 return !(value == null); 20 } 21 }
1 /// <summary> 2 /// 約束字符串的長度范圍 3 /// </summary> 4 public class StringRangeAttribute : BaseAttribute 5 { 6 public int min { get; set; } 7 public int max { get; set; } 8 public override string error { 9 get { 10 if (base.error != null) 11 { 12 return base.error; 13 } 14 return $"字符串長度范圍{this.min}-{this.max}"; 15 } 16 set => base.error = value; } 17 public override bool Validate(object value) 18 { 19 return value.ToString().Length >= this.min && value.ToString().Length <= this.max; 20 } 21 }
1 /// <summary> 2 /// 約束符合正則表達式 3 /// </summary> 4 public class RegexAttribute : BaseAttribute 5 { 6 public string regexText; 7 public override bool Validate(object value) 8 { 9 var regex = new Regex(regexText); 10 return regex.Match(value.ToString()).Success; 11 } 12 }
3、給實體類擴展一個方法,用來驗證實體類實例屬性值是否合法
此方式的擴展方法會污染其他非實體類,不是太推薦用太多此種擴展寫法,其實這里不寫成擴展方法也沒什么問題。
此擴展方法會驗證實例中的所有屬性,並將所有不通過驗證的屬性的提示信息以字符串的形式返回,所有都驗證通過則返回空串。
1 public static string Validate<T>(this T t) 2 { 3 Type type = t.GetType(); 4 5 //獲取所有屬性 6 PropertyInfo[] propertyInfos = type.GetProperties(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic); 7 List<string> errorList = new List<string>(); 8 foreach (PropertyInfo propertyInfo in propertyInfos) 9 { 10 if(propertyInfo.IsDefined(typeof(BaseAttribute)))//如果屬性上有定義該屬性,此步沒有構造出實例 11 { 12 foreach (BaseAttribute attribute in propertyInfo.GetCustomAttributes(typeof(BaseAttribute))) 13 { 14 if (!attribute.Validate(propertyInfo.GetValue(t, null))) 15 { 16 errorList.Add( $"[{propertyInfo.Name}]" + attribute.error); 17 } 18 } 19 20 } 21 } 22 return string.Join(",", errorList); 23 }
4、定義一個實體類,並測試(控制台程序)。
假設有Student.cs ,在其id屬性中添加Required特性,name屬性上添加StringRange和Regex特性。
1 public class Student 2 { 3 [Required(error = "id 不能為空!")] 4 public int? id { get; set; } 5 [Regex(regexText = "^a.*a$",error = "屬性不合格規則")] 6 [StringRange(min =5,max =10)] 7 public string name { get; set; } 8 }
1 static void Main(string[] args) 2 { 3 Student student = new Student() 4 { 5 id = null, 6 name = "ajiudsagasgasgaxb" 7 }; 8 string errorStr = student.Validate(); 9 Console.WriteLine(errorStr); 10 Console.ReadKey(); 11 }
測試結果
此代碼純屬自己理解的原理性還原,代碼可能有多處寫的不嚴謹。