zoukankan      html  css  js  c++  java
  • .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,有机会再写文章介绍了。

  • 相关阅读:
    Atitit 华为基本法 attilax读后感
    Atitit 华为管理者内训书系 以奋斗者为本 华为公司人力资源管理纲要 attilax读后感
    Atitit 项目版本管理gitflow 与 Forking的对比与使用
    Atitit 管理的模式扁平化管理 金字塔 直线型管理 垂直管理 水平管理 矩阵式管理 网状式样管理 多头管理 双头管理
    Atitit 乌合之众读后感attilax总结 与读后感结构规范总结
    深入理解 JavaScript 异步系列(4)—— Generator
    深入理解 JavaScript 异步系列(3)—— ES6 中的 Promise
    深入理解 JavaScript 异步系列(2)—— jquery的解决方案
    深入理解 JavaScript 异步系列(1)——基础
    使用 github + jekyll 搭建个人博客
  • 原文地址:https://www.cnblogs.com/happyframework/p/3074410.html
Copyright © 2011-2022 走看看