自定義一個簡單的資料模型驗證器類,可用於各類驗證場景,待後續完善

夢在旅途發表於2019-01-29

如題,自定義一個簡單的資料模型驗證器類(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

相關文章