• 推荐
  • 评论
  • 收藏

使用TransactionScope实现事务

2022-11-05    4657次浏览

 之前听同事说起C#.NET在2.0的时候就有一个“事务池”的东西,今天抽空在网上找了一下,接着翻一翻MSDN,发现了这个类——TransactionScope,字面上理解是叫“事务范围”,这个听起来比较农,在网上看到的一个比较好听的叫法是“分布式事务”,比较给力!

     之所以去了解这个类,是因为之前一直在想一个关于三层结构的问题,思考如下:理论上DAL(数据访问层)要做的事情就是提供对数据的原子操作,例如添加一个用户(往一张记录用户信息的表中插入一行数据,或者在某个xml文件中加入一个节点)、删除一个用户、删除一个用户组中的所有用户等等...,而BLL(业务逻辑层)中要做的事情就是调用DAL层的代码,在不需要了解具体的数据读写的情况下按照给定的业务规则间接对数据进行操作以实现理想中的业务逻辑,通常是调用不同的DAL类的不同方法,组合出一个业务逻辑,由此形成一个BLL类的方法。问题就出现在这里,DAL层(这里考虑数据来源是数据库,例如SqlServer)的方法通常遵循着一个规则,在开始对数据库进行操作前才打开链接,对数据库操作完毕之后果断断开链接。现在考虑这样一个业务,删除一个文章分类的时候把属于该分类的文章也一并删除,这时候我们很自然的想到了事务,例如写一个存储过程,或者使用ado.net中的事务类,很轻易的就能做到这一点,但这就出现了一种现象——在DAL层中出现了和业务逻辑有关的代码,并且整个事务其实都是在一个(确切说是“一次”)链接中完成的!

    一种理想的状态就是DAL层只提供数据的单一操作(增删查改),然后在BLL层中调用这些操作组合出一个事务级别的业务(不知道我这样描述会不会很搓- -!),这就出现了多次链接数据库进行操作了。但是每一次DAL层的操作都是从打开链接开始到断开链接结束,多个方法就是多次打开链接和多次断开链接的操作,为了实现这种级别的事务可以利用TransactionScope类,这个类的特点是实现了IDisposable接口,可以把它被实例化开始到被Dispose掉之间的代码作为一个事务,不管你数据库链接和断开了多少次,都可以实现事务。我简单地写了点代码做了测试,测试代码如下:

    实现编写一个很微型的数据库访问帮助类, 主要是封装一下对SqlCommand.ExecuteNonQuery()方法的使用,代码大致如下:

// 数据库操作帮助类
public class DB
{
    private readonly string _conStr = "Data Source=.;Initial Catalog=TiuTest;pooling=true;Persist Security Info=True;User ID=sa;Password=******";
    private System.Data.SqlClient.SqlConnection _con;
    private System.Data.SqlClient.SqlCommand _cmd;
    // 构造函数
    public DB()
    {
        _con = new System.Data.SqlClient.SqlConnection();
        _con.ConnectionString = _conStr;
        _cmd = _con.CreateCommand();
    }
    // 执行sql语句返回受影响行数
    public int ExecuteNonQuery(string CommandText)
    {
        try
        {
            _cmd.CommandText = CommandText;
            _con.Open();
            return _cmd.ExecuteNonQuery();
        }
        catch { return -1; }
        finally { this._con.Close(); }
    }
}

在控制台编写以下代码,模拟了两次调用DAL层的方法,发现在Table表中成功插入两条记录,如果把其中一个sql语句改成会报错的情况,可以发现操作被回滚了,一条记录都没有插进去。

static void Main(string[] args)
{
    try
    {
        TestTransactionScope();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
      
    Console.ReadKey();
}
static void TestTransactionScope()
{
    DB db = new DB();
    using (System.Transactions.TransactionScope tranScope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required))
    {
        int flag = 0; // 操作结果标识
        int completedFlag = 2; // 全部执行成功的操作标识(用于和变量flag做对比,相等表示sql操作全部成功)
        DateTime dateTime = DateTime.Now; // 获取当前时间
        string sql = string.Empty; // 用于存放sql语句的变量
        // 第一次执行sql,执行完将断开数据库链接
        sql = string.Format("insert into Table(Col1,Col2) values('{0}','{1}')"
            , dateTime.ToString("yyyyMMddHHmmssffff")
            , dateTime);
        flag += db.ExecuteNonQuery(sql);
        // 第二次执行sql,执行完将断开数据库链接
        dateTime = dateTime.AddDays(1);
        sql = string.Format("insert into Table(Col1,Col2) values('{0}','{1}')"  // 把这个sql改成会报错的sql语句再进行尝试!
            , dateTime.ToString("yyyyMMddHHmmssffff")
            , dateTime);
        flag += db.ExecuteNonQuery(sql);
        // 对比操作标识,决定是否提交事务
        if (flag == completedFlag)
        {
            tranScope.Complete(); // 提交事务
            Console.WriteLine("Complated!");
        }
        else
        {
            Console.WriteLine("Error!");
        }
    }
}

以上说明这种想法是可以在C#.NET中实现的,不过第一次使用的时候有可能会报错,最大的可能是有个叫做MSDTC服务的东东没有开启(控制面板-管理工具-服务-MSDTC服务-开启之),这个就是所谓的DTC分布式服务了,之所以多个不同的数据库链接操作可以组成一个事务,就是靠这个服务支持,启用DTC的不好之处是性能很低下,打断点调试过程中可以明显感觉到,并且依靠服务来完成总觉得很不方便,具体说不清,没怎么研究。

    TransactionScope的方法很好,具体可以查一下msdn,但是可以完成的功能很强大,使用的时候要要比较注意,特别是出现事务嵌套的时候更是要。具体如果你有兴趣的话可以试试,我也没有在实际项目中使用过...

    参考资料如下:

// http://www.cnblogs.com/zhangpengshou/archive/2009/07/20/1527269.html
// http://www.blogjava.net/Martin-Liu-Hai-Shi/articles/260286.html
// http://book.51cto.com/art/200807/80439.htm // 这个链接好像没用了
// http://www.cnblogs.com/xrinehart/archive/2006/10/23/537023.html DTC分布式事务、事务级别等
// http://www.cnblogs.com/zhangpengshou/archive/2009/07/20/1527269.html
// http://www.blogjava.net/Martin-Liu-Hai-Shi/articles/260286.html
// http://book.51cto.com/art/200807/80439.htm // 这个链接好像没用了
// http://www.cnblogs.com/xrinehart/archive/2006/10/23/537023.html DTC分布式事务、事务级别等
// http://www.cnblogs.com/zhangpengshou/archive/2009/07/20/1527269.html
// http://www.blogjava.net/Martin-Liu-Hai-Shi/articles/260286.html
// http://book.51cto.com/art/200807/80439.htm // 这个链接好像没用了
// http://www.cnblogs.com/xrinehart/archive/2006/10/23/537023.html DTC分布式事务、事务级别等相关
// http://www.cnblogs.com/zhangpengshou/archive/2009/07/20/1527269.html
// http://www.blogjava.net/Martin-Liu-Hai-Shi/articles/260286.html
// http://book.51cto.com/art/200807/80439.htm // 这个链接好像没用了
// http://www.cnblogs.com/xrinehart/archive/2006/10/23/537023.html DTC分布式事务、事务级别等相关
// http://www.blogjava.net/Martin-Liu-Hai-Shi/articles/260286.html
// http://book.51cto.com/art/200807/80439.htm // 这个链接好像没用了
// http://www.cnblogs.com/xrinehart/archive/2006/10/23/537023.html DTC分布式事务、事务级别等相关

 http://www.cnblogs.com/zhangpengshou/archive/2009/07/20/1527269.html

 http://www.blogjava.net/Martin-Liu-Hai-Shi/articles/260286.html

 http://book.51cto.com/art/200807/80439.htm

 http://www.cnblogs.com/xrinehart/archive/2006/10/23/537023.html

code :http://www.youguanbumen.net/Articles.aspx

原文地址:https://www.cnblogs.com/Leo_wl/p/1979435.html