.NET:可擴展的單據編號生成器 之 順序號(防止重復)


背景

我在上篇文章“.NET:可擴展的單據編號生成器 + 簡單的解釋器”中介紹了一個簡單的單據編號框架。有朋友留言問如何實現“順序號,且不能重復”,本篇文章就針對這個問題用上篇介紹的框架進行實現。

思路

順序號 = 上次順序號 + 步長

根據上面的公式,問題可以化解為:如何獲取上次順序號?獲取上次順序號有兩種方式:

    1. 掃描單據表,找出最新的一條記錄。
    2. 引入種子表,種子表記錄了最新的順序號。

因為生成的順序號不能重復,這里就有了並發的要求,為了最大限度的提高並發性,我選擇2(引入種子表)。

並發處理可以選擇:悲觀鎖或樂觀鎖,這里為了簡單,我選擇悲觀鎖

實現

代碼下載:http://yunpan.cn/Q5KMUTA3qGPct

種子表設計

1 CREATE TABLE [dbo].[CodeSeeds] (
2     [Id]    UNIQUEIDENTIFIER NOT NULL,
3     [Key]   NVARCHAR (500)   NOT NULL,
4     [Value] INT              NOT NULL,
5     PRIMARY KEY CLUSTERED ([Id] ASC)
6 );

SeedCodeRuleProvider.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.Transactions;
 8 using System.Text.RegularExpressions;
 9 using System.Data.Entity.Infrastructure;
10 
11 namespace EntityCodeRuleDemo
12 {
13     public class SeedCodeRuleProvider : ICodeRuleProvider
14     {
15         private readonly int _width;
16 
17         public SeedCodeRuleProvider(int width)
18         {
19             _width = width;
20         }
21 
22         public string Generate(object entity)
23         {
24             return GetSeedValue(entity).ToString().PadLeft(_width, '0');
25         }
26 
27         protected virtual string GetKey(object entity)
28         {
29             return entity.GetType().FullName;
30         }
31 
32         private int GetSeedValue(object entity)
33         {
34             try
35             {
36                 var value = 0;
37                 var key = this.GetKey(entity);
38 
39                 using (var ts = new TransactionScope(TransactionScopeOption.RequiresNew,
40                     new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead }))
41                 {
42                     using (var context = new TestContext())
43                     {
44                         var seed = context.CodeSeeds.Where(x => x.Key == key).FirstOrDefault();
45                         if (seed == null)
46                         {
47                             seed = new CodeSeed { Id = Guid.NewGuid(), Key = key, Value = -1 };
48                             context.CodeSeeds.Add(seed);
49                         }
50 
51                         seed.Value++;
52                         value = seed.Value;
53                         context.SaveChanges();
54                     }
55 
56                     ts.Complete();
57                 }
58 
59                 return value;
60             }
61             catch (DbUpdateException)
62             {
63                 return this.GetSeedValue(entity);
64             }
65         }
66 
67         public static SeedCodeRuleProvider SeedCodeRuleProviderFactory(string literal)
68         {
69             var match = new Regex("^<種子(:(?<寬度>.*?))?>$").Match(literal);
70 
71             var width = match.Groups["寬度"].Value;
72 
73             return new SeedCodeRuleProvider(string.IsNullOrEmpty(width) ? 5 : int.Parse(width));
74         }
75     }
76 }

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 using System.Text.RegularExpressions;
 8 
 9 namespace EntityCodeRuleDemo
10 {
11     class Program
12     {
13         static void Main(string[] args)
14         {
15             CodeRuleInterpreter.RegistProviderFactory(new Regex("^<種子(:(?<寬度>.*?))?>$"), SeedCodeRuleProvider.SeedCodeRuleProviderFactory);
16 
17             var employeeCode = CodeRuleInterpreter
18                 .Interpret("前綴_<日期:yyyy_MM_dd>_<屬性:NamePinYin>_<種子:6>")
19                 .Generate(new Employee { NamePinYin = "DUANGW" });
20 
21             Console.WriteLine(employeeCode);
22         }
23     }
24 }

運行結果

備注

有寫業務要求會強制編號的連續性或編號的隨機性,對於這兩種需求,還需要單獨開發Provider,有機會再寫文章介紹了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM