背景
在企業應用中單據編號的自定義是一個很常見的需求,能不能抽象一個通用的框架呢?之前寫個一篇自定義密碼強度的博文,感覺他們兩個思路應該很相似。就讓我們試試吧。
思路
這里的難點在於實現"解釋器",比如將"前綴_<日期:yyyy_MM_dd>"解釋為“工號生成器”,而且“解釋器”的“規則”允許動態增加。
實現
代碼下載
類圖
核心代碼
CodeRuleGenerator.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 using System.Text.RegularExpressions; 8 9 namespace EntityCodeRuleDemo 10 { 11 public sealed class CodeRuleGenerator : ICodeRuleGenerator 12 { 13 private readonly IEnumerable<ICodeRuleProvider> _providers = new List<ICodeRuleProvider>(); 14 15 internal CodeRuleGenerator(IEnumerable<ICodeRuleProvider> providers) 16 { 17 _providers = providers; 18 } 19 20 public string Generate(object entity) 21 { 22 var sb = new StringBuilder(); 23 24 foreach (var provider in _providers) 25 { 26 sb.Append(provider.Generate(entity)); 27 } 28 29 return sb.ToString(); 30 } 31 } 32 }
CodeRuleInterpreter.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 using System.Text.RegularExpressions; 8 9 using EntityCodeRuleDemo.RuleProviders; 10 11 namespace EntityCodeRuleDemo 12 { 13 public static class CodeRuleInterpreter 14 { 15 private static Dictionary<Regex, Func<string, ICodeRuleProvider>> _providerFactorys = new Dictionary<Regex, Func<string, ICodeRuleProvider>>(); 16 17 static CodeRuleInterpreter() 18 { 19 SetProviderFactory(new Regex("^[^<].*?[^>]?$"), LiteralRuleProvider.LiteralRuleProviderFactory); 20 SetProviderFactory(new Regex("^<日期(:(?<格式>.*?))?>$"), DateRuleProvider.DateRuleProviderFactory); 21 SetProviderFactory(new Regex("^<屬性(:(?<名稱>.*?))?>$"), PropertyRuleProvider.PropertyRuleProviderFactory); 22 } 23 24 public static void SetProviderFactory(Regex regex, Func<string, ICodeRuleProvider> providerFactory) 25 { 26 _providerFactorys[regex] = providerFactory; 27 } 28 29 30 public static ICodeRuleGenerator Interpret(string codeRule) 31 { 32 var providers = GetProviders(codeRule); 33 34 return new CodeRuleGenerator(providers); 35 } 36 37 private static IEnumerable<ICodeRuleProvider> GetProviders(string codeRule) 38 { 39 var literals = codeRule.Replace("<", "$<").Replace(">", ">$").Split('$'); 40 41 return literals 42 .Where(x => !string.IsNullOrEmpty(x)) 43 .Select(GetProvider) 44 .ToList(); 45 } 46 47 private static ICodeRuleProvider GetProvider(string literal) 48 { 49 var providerFactory = _providerFactorys 50 .FirstOrDefault(x => x.Key.IsMatch(literal)) 51 .Value; 52 53 if (providerFactory == null) 54 { 55 throw new FormatException("格式化錯誤"); 56 } 57 58 return providerFactory(literal); 59 } 60 } 61 }
Program.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace EntityCodeRuleDemo 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 var employeeCode = CodeRuleInterpreter 14 .Interpret("前綴_<日期:yyyy_MM_dd>_<屬性:NamePinYin>") 15 .Generate(new Employee { NamePinYin = "DUANGW" }); 16 17 Console.WriteLine(employeeCode); 18 } 19 } 20 21 class Employee 22 { 23 public string NamePinYin { get; set; } 24 public string EmployeeCode { get; set; } 25 } 26 }
運行效果
備注
按照這種思路,基本上能滿足企業應用的多數編碼規則要求。在真實的項目中,這些規則是要持久化到數據庫的,這樣就可以做到運行時動態的修改規則了。