[C#]实现序列号生成器

来自:http://www.cnblogs.com/leafyoung/archive/2005/10/10/251436.html

对于数据库应用而言, 一个合适的序列号生成器是非常有用的, 用它我们可以方便地给新增的记录的id字段(主键)赋值, 当然我们同样可以用Identity类型主键, 但是假如同时存在父子关系的记录, 那么给子记录赋parent_id就极为不便, 我们必须先将父记录插入到数据库, 然后从数据库中取得自动生成的id, 然后才成给子记录的parent_id字段赋值. 毫无疑问, 假如我们可以给父记录的id直接赋值的话, 问题就简单得多了.
    但是, 对于一个分布式应用而言, 不经过周密的考虑往往会带来id重复的问题. 在我们公司既有的产品中采用段号来解决这个问题, 就是说为每个用户分配一个足够长的区间(比如10000000)作为可选id, 每个用户使用独立的区间的id一般不会带来问题, 但是作为一个完美主义者来说, 不能不考虑到一个极端情况, 假如区间重叠了怎么办? 而且这样的分配方式无疑极大地浪费了id资源, 是否有更好的解决方案呢?
    前几天阅读Martin Fowler的大作<Patterns of Enterprise Application Architecture>, 其中提到了一种不错的解决方案, 即使利用 键表(key table), 典型的键表有两列: 名字(name)和下一个有效id(nextID). 如果使用数据库唯一键的话, 这个表只有一条记录, 如"DB, 1"; 如果使用表唯一键, 那么对于每个数据库表都有一行数据与之对应, 比如: "Users, 11", "Roles, 31"等等. 我们要获得一个新的可用id, 只需要从一个合适的行中获得nextID值, 然后把nextID + incrementBy存入数据库, 这里每次加上一个incrementBy是为了减少数据库的操作, 避免浪费宝贵的数据库连接资源, 减少冲突发生的可能行.
    下面看看我在.net下的实现(模仿Martin Fowler的大作):

 

using  System; 
using  System.Diagnostics; 
using  System.Data; 
using  System.Data.Common;

namespace  MyProject.Core
{
    
public   class  KeyGenerator
    {
        
#region  private members

        
private  IDbConnection _connection  =   null ;
        
private   string  _keyName;
        
private   int  _incrementBy;
        
private   long  _nextId  =   0 ;
        
private   long  _maxId  =   0 ;

        
#endregion

        
public  KeyGenerator(IDbConnection conn,  string  keyName,  int  incrementBy)
        {
            _connection 
=  conn;
            _keyName 
=  keyName;
            _incrementBy 
=  incrementBy;
        }

        
public   long  NextKey
        {
            
get
            {
                
if  (_nextId  ==  _maxId)
                {
                    reserveIds();
                }
                
return  _nextId ++ ;
            }
        }

        
private   void  reserveIds()
        {
            IDbTransaction tx 
=  _connection.BeginTransaction();
            IDbCommand cmd 
=  _connection.CreateCommand();
            cmd.Transaction 
=  tx;
            
try
            {
                cmd.CommandText 
=   string .Format(
                    
" update keys set nextID = nextID + {0} where name = \'{1}\' " ,
                    _incrementBy, _keyName);
                
int  result  =  cmd.ExecuteNonQuery();
                Debug.Assert(result 
==   1 , _keyName  +   "  not exists " );
                cmd.CommandText 
=   string .Format( " select nextID from keys where name = \'{0}\' " ,
                    _keyName);
                _maxId 
=  Convert.ToInt64(cmd.ExecuteScalar());
                _nextId 
=  _maxId  -  _incrementBy;
                tx.Commit();
            }
            
catch  (Exception ex)
            {
                tx.Rollback();
                
throw  ex;
            }
            
finally
            {
                cmd.Dispose();
            }
        }
    }
}

 

使用的方法也很简单, 如下所示:

 

OleDbConnection conn  =   new  OleDbConnection(); 
conn.ConnectionString 
=   
    
" Provider=SQLOLEDB;Data Source=(local);Initial Catalog=gcs;Trusted_Connection=Yes "
conn.Open(); 
 
KeyGenerator gen 
=   new  KeyGenerator(conn,  " Users " 10 ); 
 
for  ( int  i  =   0 ; i  <   10 ; i ++

    Console.WriteLine(gen.NextKey); 

 
conn.Close(); 

 

附: 创建键表的sql

CREATE TABLE [keys] ( 
    [name] [varchar] (
32 ) COLLATE Chinese_PRC_CI_AS NOT NULL , 
    [nextID] [
int ] NULL , 
     PRIMARY KEY  CLUSTERED  
    ( 
        [name] 
    )  ON [PRIMARY]  
) ON [PRIMARY] 


 

转载于:https://www.cnblogs.com/hanwater/archive/2009/05/09/1453049.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值