如題,自定義一個簡單的資料模型驗證器類(ModelValidator),目前只有提供基本的手動呼叫驗證方法進行驗證,並最終輸出驗證結果,待後續完善,增加基於特性的自動驗證並輸出驗證結果的功能,程式碼實現比較簡單,直接貼出原始碼:
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; namespace Zuowj.Common { /// <summary> /// 模型驗證器類(用於驗證實體類的相關屬性是否符合驗證規則) /// Author:zuowenjun /// Date:2018-5-25 /// </summary> public class ModelValidator { private StringBuilder errorMsgBuilder = new StringBuilder(); private bool isValid = true; public bool IsValid { get { return isValid; } } public bool IsNotNullOrWhiteSpace(string str, string fieldName) { bool result = string.IsNullOrWhiteSpace(str); if (result) { errorMsgBuilder.AppendFormat("{0}不能為空;{1}", fieldName, Environment.NewLine); isValid = false; } return result; } public bool IsNumeric(string numStr, string fieldName) { decimal numValue; bool result = decimal.TryParse(numStr, out numValue); if (!result) { errorMsgBuilder.AppendFormat("{0}不是數字;{1}", fieldName, Environment.NewLine); isValid = false; } return result; } public bool IsDateTime(string timeStr, string fieldName) { DateTime time; bool result = DateTime.TryParse(timeStr, out time); if(!result) { errorMsgBuilder.AppendFormat("{0}不是日期;{1}", fieldName, Environment.NewLine); isValid = false; } return result; } public bool IsGreaterThan<T>(T fieldValue, T compareValue, bool allowEqual, string fieldName) where T : IComparable { int compResult = fieldValue.CompareTo(compareValue); bool result = false; if (compResult > 0 || (allowEqual && compResult >= 0)) { result = true; } else { errorMsgBuilder.AppendFormat("{0}必需{1}{2};{3}", fieldName, allowEqual ? "大於或等於" : "大於", compareValue, Environment.NewLine); result = false; isValid = false; } return result; } public bool IsLessThan<T>(T fieldValue, T compareValue, bool allowEqual, string fieldName) where T : IComparable { int compResult = fieldValue.CompareTo(compareValue); bool result = false; if (compResult < 0 || (allowEqual && compResult <= 0)) { result = true; } else { errorMsgBuilder.AppendFormat("{0}必需{1}{2};{3}", fieldName, allowEqual ? "小於或等於" : "小於", compareValue, Environment.NewLine); result = false; isValid = false; } return result; } public bool HasItem<T>(IEnumerable<T> list, string fieldName) { bool result = true; if (list == null || !list.Any()) { result = false; errorMsgBuilder.AppendFormat("{0}沒有任何集合;{1}", fieldName, Environment.NewLine); isValid = false; } return result; } public bool CustomValidate<T>(T fieldValue, Func<T, string> validateFunc) { string errorMsg = validateFunc(fieldValue); if (!string.IsNullOrWhiteSpace(errorMsg)) { return AppendErrorMessage(errorMsg); } else { return true; } } public bool AppendErrorMessage(string errorMsg) { errorMsgBuilder.AppendLine(errorMsg); isValid = false; return false; } public string GetErrorMessage() { return errorMsgBuilder.ToString(); } public override string ToString() { return errorMsgBuilder.ToString(); } } }
從程式碼可以看出,基本上只是提供了一些常見的邏輯驗證方法而矣,並把驗證的結果返回,同時組合驗證的錯誤結果資訊,以便最終可以輸出完整的驗證失敗的資訊。
用法很簡單,new一個ModelValidator,然後根據自己的MODEL驗證規則,呼叫對應的驗證方法,最終輸出驗證結果即可,如下示例:
//BLL層:(驗證呼叫入口) var validateResult = CheckFreightChargeReqDto(requestDto, tranType); if (!validateResult.IsValid) { throw new Exception(validateResult.GetErrorMessage()); } //具體的驗證邏輯:(自定義封裝的驗證方法,取決於個人) private ModelValidator CheckFreightChargeReqDto(FreightChargeReqDto requestDto, FreightChargeTranType tranType) { ModelValidator validator = new ModelValidator(); if (tranType == FreightChargeTranType.Calculate) { validator.IsNotNullOrWhiteSpace(requestDto.CustomerName, "客戶名稱"); } validator.IsNotNullOrWhiteSpace(requestDto.ServiceType, "服務方式"); validator.IsNotNullOrWhiteSpace(requestDto.FromZoneNo, "始發區號"); validator.IsNotNullOrWhiteSpace(requestDto.ToZoneNo, "目的區號"); validator.IsNotNullOrWhiteSpace(requestDto.ProjectNo, "專案編號"); if (validator.HasItem(requestDto.SpecQueryItems, "規格查詢列表")) { if (requestDto.SpecQueryItems.Any(t => string.IsNullOrWhiteSpace(t.SpecNo))) { validator.AppendErrorMessage("規格編號不能為空;"); } if (requestDto.SpecQueryItems.Any(t => string.IsNullOrEmpty(t.SpecName) && string.IsNullOrEmpty(t.SpecNo))) { validator.AppendErrorMessage("規格編號、規格名稱兩者必需有1個不能為空;"); } else { var specQueryItemWithSpecNos = requestDto.SpecQueryItems.Where(t => !string.IsNullOrEmpty(t.SpecNo)); if (specQueryItemWithSpecNos.Count() > specQueryItemWithSpecNos.Select(t => t.SpecNo).Distinct().Count()) { validator.AppendErrorMessage("規格編號存在重複;"); } var specQueryItemWithSpecNames = requestDto.SpecQueryItems.Where(t => !string.IsNullOrEmpty(t.SpecName) && string.IsNullOrEmpty(t.SpecNo)); if (specQueryItemWithSpecNames.Count() > specQueryItemWithSpecNames.Select(t => t.SpecName).Distinct().Count()) { validator.AppendErrorMessage("規格名稱存在重複;"); } } if (validator.IsValid) { validator.IsLessThan(requestDto.SpecQueryItems.Count, 20, true, "規格批量查詢數"); } } return validator; } public class FreightChargeReqDto { public int TranType { get; set; } public string CustomerName { get; set; } public string ServiceType { get; set; } public string FromZoneNo { get; set; } public string ToZoneNo { get; set; } public string ProjectNo { get; set; } public List<ECProjectSpecQueryItem> SpecQueryItems { get; set; } public class ECProjectSpecQueryItem { public string SpecNo { get; set; } public string SpecName { get; set; } public int PCS { get; set; } public double Weight { get; set; } public double CubicQty { get; set; } } }
由於使用簡單且功能還有待進一步完善,故在此就不再詳細說明,後續若完善了再補充說明吧。
如果是ASP.NET MVC 或 ASP.NET CORE 建議可以參考我之前的文章:《ASP.NET MVC必知必會知識點總結(二)》 文末總結的幾種對於MVC的模型驗證的方法;
另外推薦一個已有的驗證元件:FluentValidation,這個類似於我本文實現的效果,但人家做得比較完善,將每個驗證的邏輯變成一個個自定義的驗證Rule集合,大家有興趣的可以看一下:https://github.com/JeremySkinner/FluentValidation