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,有机会再写文章介绍了。

  • 相关阅读:
    3个同一行div的摆放div
    Android 项目开发
    iOS 8
    iOS 8
    __FILE__ 与 $_SERVER['SCRIPT_FILENAME']的区别
    高德百度坐标系转换方法
    高德百度坐标系转换方法
    在iOS开发中使用icon font的方法
    在iOS开发中使用icon font的方法
    UIView的setNeedsLayout, layoutIfNeeded 和 layoutSubviews 方法之间的关系解释
  • 原文地址:https://www.cnblogs.com/happyframework/p/3074410.html
Copyright © 2011-2022 走看看