歡迎您光臨本站 註冊首頁

關於.NET Attribute在數據校驗中的應用教程

←手機掃碼閱讀     ljg58026 @ 2020-06-12 , reply:0

            前言               

Attribute(特性)的概念不在此贅述了,相信有點.NET基礎的開發人員都明白,用過Attribute的人也不在少數,畢竟很多框架都提供自定義的屬性,類似於Newtonsoft.JSON中JsonProperty、JsonIgnore等

            自定義特性               

.NET 框架允許創建自定義特性,用於存儲聲明性的信息,且可在運行時被檢索。該信息根據設計標準和應用程序需要,可與任何目標元素相關。

創建並使用自定義特性包含四個步驟:

  • 聲明自定義特性

  • 構建自定義特性

  • 在目標程序元素上應用自定義特性

  • 通過反射訪問特性

     聲明自定義特性        

一個新的自定義特性必須派生自System.Attribute類,例如:

  public class FieldDescriptionAttribute : Attribute  {    public string Description { get; private set; }      public FieldDescriptionAttribute(string description)    {      Description = description;    }  }

   

  public class UserEntity  {    [FieldDescription("用戶名稱")]    public string Name { get; set; }  }

   

該如何拿到我們標註的信息呢?這時候需要使用反射獲取

     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特性妙用        

在實際開發過程中,我們的系統總會提供各種各樣的對外介面,其中參數的校驗是必不可少的一個環節。然而沒有特性時,校驗的代碼是這樣的:

   public class UserEntity   {     ////// 姓名     ///[FieldDescription("用戶名稱")]     public string Name { get; set; }       ////// 年齡     ///public int Age { get; set; }       ////// 地址     ///public string Address { get; set; }   }

   

     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("地址不能為空");     }

   

欄位多了之後這種代碼就看著非常繁瑣,並且看上去不直觀。對於這種繁瑣又噁心的代碼,有什麼方法可以優化呢?    
    使用特性後的驗證寫法如下:

首先定義一個基礎的校驗屬性,提供基礎的校驗方法

    public abstract class AbstractCustomAttribute : Attribute    {      ////// 校驗後的錯誤信息      ///public string ErrorMessage { get; set; }        ////// 數據校驗      //////public abstract void Validate(object value);    }

   

然後可以定義常用的一些對應的校驗Attribute,例如RequiredAttribute、StringLengthAttribute

      ////// 非空校驗      ///[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);          }        }      }        ////// 自定義驗證,驗證字元長度      ///[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

  public static class ValidateExtensions   {     ////// 校驗     /////////public static void Validate(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));         }       }     }   }

   

新的實體類

   public class UserEntity   {     ////// 姓名     ///[Required]     public string Name { get; set; }       ////// 年齡     ///public int Age { get; set; }       ////// 地址     ///[Required]     public string Address { get; set; }       [StringLength(11, 11)]     public string PhoneNum { get; set; }   }

   

調用方式

  UserEntity user = new UserEntity();  user.Validate();

   

上面的校驗邏輯寫的比較複雜,主要是考慮到對象中包含複雜對象的情況,如果都是簡單對象,可以不用考慮,只需針對單個屬性做欄位校驗

現有的方式是在校驗不通過的時候拋出異常,此處大家也可以自定義異常來表示校驗的問題,也可以返回自定義的校驗結果實體來記錄當前是哪個欄位出的問題,留待大家自己實現

如果您有更好的建議和想法歡迎提出,共同進步

 


[ljg58026 ] 關於.NET Attribute在數據校驗中的應用教程已經有235次圍觀

http://coctec.com/docs/vscode/show-post-238175.html