xml地图|网站地图|网站标签 [设为首页] [加入收藏]
同步两张表的数据小技巧,中回滚TransactionScope的
分类:编程

TransactionScope只要一个操作失败,它会自动回滚,Complete表示事务完成

 

实事上,一个错误的理解就是Complete()方法是提交事务的,这是错误的,事实上,它的作用的表示本事务完成,它一般放在try{}的结尾处,不用判断前台操作是否成功,如果不成功,它会自己回滚。

 

 

在.net 1.1的时代,还没有TransactionScope类,因此很多关于事务的处理,都交给了SqlTransaction和SqlConnection,每个Transaction是基于每个Connection的。这种设计对于跨越多个程序集或者多个方法的事务行为来说,不是非常好,需要把事务和数据库连接作为参数传入。

在.net 2.0后,TransactionScope类的出现,大大的简化了事务的设计。示例代码如下:

  1.  

    static void Main(string[] args)

  2.  

    {

  3.  

    using (TransactionScope ts = new TransactionScope())

  4.  

    {

  5.  

    userBLL u = new userBLL();

  6.  

    TeacherBLL t = new TeacherBLL();

  7.  

    u.ADD();

  8.  

    t.ADD();

  9.  

    ts.Complete();

  10.  

    }

  11.  

    }

只需要把需要事务包裹的逻辑块写在using (TransactionScope ts = new TransactionScope())中就可以了。从这种写法可以看出,TransactionScope实现了IDispose接口。除非显示调用ts.Complete()方法。否则,系统不会自动提交这个事务。如果在代码运行退出这个block后,还未调用Complete(),那么事务自动回滚了。在这个事务块中,u.ADD()方法和t.ADD()方法内部都没有用到任何事务类。

TransactionScope是基于当前线程的,在当前线程中,调用Transaction.Current方法可以看到当前事务的信息。具体关于TransactionScope的使用方法,已经它的成员方法和属性,可以查看 MSDN.aspx) 。

TransactionScope类是可以嵌套使用,如果要嵌套使用,需要在嵌套事务块中指定TransactionScopeOption参数。默认的这个参数为Required。

该参数的具体含义可以参考

比如下面代码:

  1.  

    static void Main(string[] args)

  2.  

    {

  3.  

    using (TransactionScope ts = new TransactionScope())

  4.  

    {

  5.  

    Console.WriteLine(Transaction.Current.TransactionInformation.LocalIdentifier);

  6.  

    userBLL u = new userBLL();

  7.  

    TeacherBLL t = new TeacherBLL();

  8.  

    u.ADD();

  9.  

    using (TransactionScope ts2 = new TransactionScope(TransactionScopeOption.Required))

  10.  

    {

  11.  

    Console.WriteLine(Transaction.Current.TransactionInformation.LocalIdentifier);

  12.  

    t.ADD();

  13.  

    ts2.Complete();

  14.  

    }

  15.  

    ts.Complete();

  16.  

    }

  17.  

    }

当嵌套类的TransactionScope的TransactionScopeOption为Required的时候,则可以看到如下结果,他们的事务的ID都是同一个。并且,只有当2个TransactionScope都complete的时候才能算真正成功。

图片 1

如果把TransactionScopeOption设为RequiresNew,则嵌套的事务块和外层的事务块各自独立,互不影响。

  1.  

    static void Main(string[] args)

  2.  

    {

  3.  

    using (TransactionScope ts = new TransactionScope())

  4.  

    {

  5.  

    Console.WriteLine(Transaction.Current.TransactionInformation.LocalIdentifier);

  6.  

    userBLL u = new userBLL();

  7.  

    TeacherBLL t = new TeacherBLL();

  8.  

    u.ADD();

  9.  

    using (TransactionScope ts2 = new TransactionScope(TransactionScopeOption.RequiresNew))

  10.  

    {

  11.  

    Console.WriteLine(Transaction.Current.TransactionInformation.LocalIdentifier);

  12.  

    t.ADD();

  13.  

    ts2.Complete();

  14.  

    }

  15.  

    ts.Complete();

  16.  

    }

  17.  

    }

图片 2

可以看到,他们的事务id是不一样的。

 

 

TransactionScopeOption的属性值:

 

图片 3

图片 4

图片 5

对于多个不同服务器之间的数据库操作,TransactionScope依赖DTC(Distributed Transaction Coordinator)服务完成事务一致性。

但是对于单一服务器数据,TransactionScope的机制则比较复杂。主要用的的是线程静态特性。线程静态特性ThreadStaticAttribute让CLR知道,它标记的静态字段的存取是依赖当前线程,而独立于其他线程的。既然存储在线程静态字段中的数据只对存储该数据的同一线程中所运行的代码可见,那么,可使用此类字段将其他数据从一个方法传递到该第一个方法所调用的其他方法,而且完全不用担心其他线程会破坏它的工作。TransactionScope 会将当前的 Transaction 存储到线程静态字段中。当稍后实例化 SqlCommand 时(在此 TransactionScope 从线程局部存储中删除之前),该 SqlCommand 会检查线程静态字段以查找现有 Transaction,如果存在则列入该 Transaction 中。通过这种方式,TransactionScope 和 SqlCommand 能够协同工作,从而开发人员不必将 Transaction 显示传递给 SqlCommand 对象。实际上,TransactionScope 和 SqlCommand 所使用的机制非常复杂。

举个例子:

TableName:要同步的表名,UPTime每一次同步的触发时间点(可更改),sys_tamp行变更时间戳(不可更改),LastUPstamp行最后有效变量时间戳(可以更新)

 

 之前的文章同步主要是基于TranFlag标记字段 或触发器来实现同步,这种方式必需对表数据的增、删、改逻辑都有要求与规范,也就是增、改必需更改TranFlag=0,删必需记录表删除临进表中,这样才能实现同步逻辑,而今天是在这个同步基础上(BCP),不给表、DB增加额外负担的情况实时增量同步,对数据源的插入、改动没有要求。

 

上述三种方案,第1、2方案基本都是定制化的常规方案,我(梦在旅途,)今天要分享的是第3种方案:跨DB增量(增、改)同步两张表的数据,注意是增量同步,其中删除这个我没有说明,原因是如果DB表中记录是物理删除(即:真实的DELETE),那就无法简单的通过程序代码获取到删除的记录,除非在DB中加入DELETE触发器记录删除记录的主键到临时表或开启更改追踪(CHANGE_TRACKING)或DB日志分析,故本文讲的是不给表、DB增加额外负担的情况实时增量同步,至于删的同步这个我认为最好是逻辑标记删除(过期最后清理【真实删除】),而不要物理删除。

1.       他是类型安全的。实例化了int类型的栈,就不能处理string类型的数据,其他数据类型也一样。

1.双方提供RESET API,需要访问不同DB数据时,可以通过API来获取指定数据;

 

1.TS_SyncUptime表用于记录与管理同步任务的信息,主要包含如下几个字段:

这个类和object实现的类有截然不同的区别:

            try
            {
                SqlConnection obConnSrc = new SqlConnection(connLMSStr);
                SqlConnection obConnDest = new SqlConnection(mconnCCSStr);

                string lastTamp = ClsDatabase.gGetFieldValue(obConnSrc, "update TS_SyncUptime set UPTime=GETDATE() OUTPUT (deleted.LastUPstamp) as oldtamp FROM TS_CCSUptime WHERE TableName=N'tableNameA'", "oldtamp");


                string selectSql = @"SELECT id,aaa,bbb,ccc,ddd,eee,fff  
                                  FROM tableNameA WHERE 其它同步过滤查询条件 AND CONVERT(bigint,sys_tamp)>{0}";

                selectSql = string.Format(selectSql, lastTamp);

                master.TransferBulkCopy(selectSql, obConnSrc,
                                "tableNameA", obConnDest,
                                 (stable) =>
                                 {
                                     var colMaps = new Dictionary<string, string>();
                                     foreach (DataColumn col in stable.Columns)
                                     {
                                         colMaps.Add(col.ColumnName, col.ColumnName);
                                     }
                                     return colMaps;
                                 },
                                 (tempTableName, stable, destConn, srcConn) =>
                                 {
                                     StringBuilder saveSqlBuilder = new StringBuilder("begin tran" + Environment.NewLine);

                                     string IUSql = master.BuildInsertOrUpdateToDestTableSql("tableNameA", tempTableName, new[] { "id" }, stable.ExtendedProperties[master.MapDestColNames_String], 2);
                                     saveSqlBuilder.Append(IUSql);

                                     saveSqlBuilder.AppendLine("commit");

                                     ClsDatabase.gExecCommand(destConn, saveSqlBuilder.ToString());


                                     ClsDatabase.gExecCommand(srcConn, "update TS_SyncUptime set UPTime=GETDATE(),LastUPstamp=CONVERT(bigint,sys_tamp) FROM TS_SyncUptime WHERE TableName=N'tableNameA'");

                                     return false;
                                 });


            }
            catch (Exception ex)
            {
                writeLog(ex);//记错误日志
            }

  

 图片 6

Dictionary<string, string>是一个泛型,什么是泛型?

这种方案的优点是:可以根据实际情况灵活定制同步的表数据,不局限于某一张表或某一个DB,可以保证不同DB间同步表的数据一致性,让本来跨DB操作表变成了同一个DB的事情,而且可以增、删、改、查,功能不受限;缺点是灵活性太强,程序代码实现可靠的跨DB的实时同步逻辑的实现复杂度较高,对于开发人员的要求较高,如果写的同步逻辑无法保证实时、可靠、高可用,那对于业务来讲是灾难性的。

/// <summary>
/// 定义Key为string类型,Value为int类型的一个Dictionary
/// </summary>
/// <returns></returns>
protected Dictionary<string, int> SetKeyValue()
{
Dictionary<string, int> dic = new Dictionary<string, int>();
dic.Add("公司1", 1);
dic.Add("公司2", 2);
dic.Add("公司3", 3);
dic.Add("公司4", 4);
return dic;
}


/// <summary>
/// 得到根据指定的Key行到Value
/// </summary>
protected void GetKeyValue()
{
Dictionary<string, int> myDictionary = SetKeyValue();
//测试得到公司2的值
int directorValue = myDictionary["公司2"];
Response.Write("公司2的value是:" + directorValue.ToString());
}

这种方案优点是隔离性、定制性强,统一出入口,只能通过指定的API访问指定的数据;缺点与优点是对立的,也就是定制性太强,导致每次业务发生变更,需要访问不同数据的时候,需要双方更改API的入参或返参,降低了开发效率;而且无法使用表JOIN,这样在某些情况下也会导致查询数据效率变低。目前主流的方案都是建议使用API方案

 

2.利用DB的同步技术(如:SQL SERVER的订阅复制、MYSQL的主从复制脚本等)来实现不同DB的数据同步共享

 

 上述同步代码逻辑很简单,可以参照之前的文章,这里主要是说明几个重要点:

本文由澳门新葡亰手机版发布于编程,转载请注明出处:同步两张表的数据小技巧,中回滚TransactionScope的

上一篇:没有了 下一篇:没有了
猜你喜欢
热门排行
精彩图文