.NET:可扩展的单据编号生成器 + 简单的解释器

背景

在企业应用中单据编号的自定义是一个很常见的需求,能不能抽象一个通用的框架呢?之前写个一篇自定义密码强度的博文,感觉他们两个思路应该很相似。就让我们试试吧。

思路

这里的难点在于实现"解释器",比如将"前缀_<日期:yyyy_MM_dd>"解释为“工号生成器”,而且“解释器”的“规则”允许动态增加。

 .NET:可扩展的单据编号生成器 + 简单的解释器_编号生成

实现

代码下载

类图

.NET:可扩展的单据编号生成器 + 简单的解释器_编号生成_02

核心代码

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 }

运行效果

.NET:可扩展的单据编号生成器 + 简单的解释器_编号生成_03

备注

按照这种思路,基本上能满足企业应用的多数编码规则要求。在真实的项目中,这些规则是要持久化到数据库的,这样就可以做到运行时动态的修改规则了。

 

框架地址:http://happy.codeplex.com