.NET 事务技术-System.Transactions.TransactionScope 隐式事务(方便)

作者:vkvi 来源:千一网络(原创) 日期:2017-2-28

前一节说了 SqlConnection 的显式事务,现在谈谈隐式事务,相对于显式事务,隐式事务不再有 Commit、Rollback 方法。

准备工作

  • 添加 System.Transactions 引用(在解决方案的项目/网站上右键);
  • 引用 System.Transactions 名称空间。

示例讲解

using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["MySqlServer"].ConnectionString))
{
    using (TransactionScope ts = new TransactionScope())
    {
        conn.Open();
        using (SqlCommand cmd = new SqlCommand())
        {
            cmd.Connection = conn;
            cmd.CommandType = CommandType.Text;
            try
            {
                cmd.CommandText = "insert into TranTable(Priority) values(1)";
                cmd.ExecuteNonQuery();
                cmd.CommandText = "insert into TranTable(Priority) values(256)";
                cmd.ExecuteNonQuery();
                ts.Complete();
                Response.Write("Ok");
            }
            catch (SqlException ex)
            {
                Response.Write("Error:" + ex.Message);
            }
        }
        conn.Close();
    }
}

可以看出 TransactionScope 并不来自于 SqlConnection,所以它可以管理多个 SqlConnection。并且它不管什么 Commit、Rollback,只管 Complete。

注意:conn.Open() 一定要在 new TransactionScope() 后面,否则不起作用,conn.Close() 则是否位于 TransactionScope 范围都无所谓。

TransactionScope 和数据库操作并没有直接关联,是什么造成了数据库操作以事务的形式执行的呢?

现在我们在数据库连接字符串中加上 Enlist=false;,再执行上面的代码,然后观察数据库,可以发现数据库中多了一条记录 1,说明上述代码不是按事务的方式执行的。原来,Enlist 默认为 true,表示 SqlClient 将自动检测是否有事务存在,若存在,则自动登记到事务中,也就形成了事务,这也是为什么 conn.Open() 要在 new TransactionScope() 后面的原因。

enlist [in'list]

v. 徵募,参与,支持

再看一个复杂点的

有两个 SqlCommand,第一个能够插入记录,第二个不能插入记录。

理论上来说,由于使用了事务,最终结果是没有插入任何数据到数据库的,但是我们发现第一个竟然插入成功了。难道是不支持两个 SqlCommand?其实并不是,而是我们的 ts.Complete() 位置不对,应该改成如下:

将 ts.Complete() 放在 try 中和 Do1()、Do2() 一起,就体现出事务的特性了,为什么会这样呢?

ts.Complete() 表示告知数据库可以写入了。

ts.Complete() 放在 try 外时:

  • Do1() 可以写入,那就写入吧。
  • Do2() 不能写入,那就不写入吧。

而再看看将 ts.Complete() 放在 try 中和 Do1()、Do2() 一起有什么神奇的事情:

  • Do1() 没发生异常。
  • Do2() 发生了异常,跳出 try。
  • 所以 ts.Complete() 根本就没有机会执行到。所以 Do1() 虽然可以写入,但是由于 ts.Complete() 没执行,就没给他写入的机会,所以 Do1()、Do2() 最终都没有写入数据库。

说一句,事务执行时,虽然 Do1() 没有写入,但是它仍然占用了一个 Id,也就是说当我们数据库中使用了标识列(通常说的自增长 Id 列),这个 Id 会出现不连续,经测试发现就是由于 Do1()(不是 Do2)写入后又撤销了造成的。

如果我们改一下这几句代码的顺序,改成如下:

  • ts.Complete();
  • Do1();
  • Do2();

此时不会出错,Do1() 会写入成功,然后在 Do2() 产生异常。

再改一下:

  • ts.Complete();
  • Do1();
  • ts.Complete();
  • Do2();

同样,Do1() 还是会写入成功,不过在第二个 ts.Complete() 时就会出错,说明 ts.Complete() 不能重复调用

再看看三种 SQL 语句位置有什么区别

(以上代码受用途限制,有些是通过构造函数赋的值,有些是通过属性赋的值,这不影响,因为通过构造函数赋值同样是对属性赋值。)

  • 第一种,把两个 SQL 语句赋给两个 SqlCommand
  • 第二种,把两个 SQL 语句赋给一个 SqlCommand,分两次执行
  • 第三种,把两个 SQL 语句合并后赋给一个 SqlCommand,一次执行

结论:

  • 这三种都可以实现事务
  • 第一种和第二种结果一样,都会出现 Id 不连续的情况(前面有说)。而第三种不会出现 Id 不连续的情况

如果在 SQL 语句中又使用 SQL Server 的事务,有没有问题?

比如我们把上面代码的 SQL 语句改成:

不会有问题,照样按事务执行。

你前面那位网友看了:GIF 还是 PNG

▲▲▲嘿,欢迎转载传播本站原创文章,尽量保留来源噢。▲▲▲

文章评论
标题:必填
内容:
本站永远终止与捏造“罪名”不支付广告费的某度联盟合作。
vkvi
vkvi

作者简介: vkvi,致力于 .NET Web 开发、移动开发的技术推广,在 .NET、SQL Server、Windows Server 等方面有深入研究和丰富经验,10 年间共计撰写文章 4000 余篇。 主持金融、国土、农业、电商等多个行业项目执行, 推行“技术提升生产力、人心决定成功率”的管理理论。 联系他