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

前一节说了 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. 徵募,参与,支持

再看一个复杂点的public void Do()

{

using (SqlConnection conn = new SqlConnection("Data Source=.; Initial Catalog=TestDb; Integrated Security=SSPI;"))

{

using (TransactionScope ts = new TransactionScope())

{

conn.Open();

try

{

Do1(conn);

Do2(conn);

}

catch(SqlException)

{

}

ts.Complete();

conn.Close();

}

}

}

private void Do1(SqlConnection conn)

{

// 正确的插入语句

using (SqlCommand cmd = new SqlCommand("insert into TestTbl(fld1) values(2)", conn))

{

cmd.ExecuteNonQuery();

}

}

private void Do2(SqlConnection conn)

{

// 错误的插入语句

using (SqlCommand cmd = new SqlCommand("insert into TestTbl(fld1) values(j)", conn))

{

cmd.ExecuteNonQuery();

}

}

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

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

{

using (TransactionScope ts = new TransactionScope())

{

conn.Open();

try

{

Do1(conn);

Do2(conn);

ts.Complete();

}

catch(SqlException)

{

}

conn.Close();

}

}

将 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

using (SqlCommand cmd = new SqlCommand("insert into TestTbl(fld1) values(2);", conn))

{

cmd.ExecuteNonQuery();

}

using (SqlCommand cmd = new SqlCommand("insert into TestTbl(fld1) values(j)", conn))

{

cmd.ExecuteNonQuery();

}

// 第二种,把两个 SQL 语句赋给一个 SqlCommand,分两次执行

using (SqlCommand cmd = new SqlCommand())

{

cmd.Connection = conn;

cmd.CommandText = "insert into TestTbl(fld1) values(2)";

cmd.ExecuteNonQuery();

cmd.CommandText = "insert into TestTbl(fld1) values(j)";

cmd.ExecuteNonQuery();

}

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

using (SqlCommand cmd = new SqlCommand("insert into TestTbl(fld1) values(2);insert into TestTbl(fld1) values(j)", conn))

{

cmd.ExecuteNonQuery();

}

(以上代码受用途限制,有些是通过构造函数赋的值,有些是通过属性赋的值,这不影响,因为通过构造函数赋值同样是对属性赋值。)第一种,把两个 SQL 语句赋给两个 SqlCommand

第二种,把两个 SQL 语句赋给一个 SqlCommand,分两次执行

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

结论:这三种都可以实现事务。

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

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

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

insert into TestTbl(fld1) values(2);

insert into TestTbl(fld1) values(j);

commit tran

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Transactions.doTran是一个方法,它用于手动处理事务。这个方法包含了一些操作,并且在操作成功后调用了ContextUtil.SetComplete()来标记事务已完成,如果操作失败则调用了ContextUtil.SetAbort()来标记事务已中止。这个方法返回一个字符串,如果所有操作都成功执行,则返回"方法执行成功",否则返回"两个方法至少有一个执行失败!"。 另外,还有一种自动处理事务的方法,可以在方法上加上[AutoComplete(true)]特性,然后使用Using语句来创建一个TransactionScope对象,这样就可以自动处理事务。在Using语句块中执行具体的操作,如果操作成功,则事务会自动提交;如果操作失败,则事务会自动回滚。 总的来说,Transactions.doTran是一个用于手动处理事务的方法,而使用System.Transactions命名空间中的TransactionScope类可以实现自动处理事务。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [asp.net事务机制](https://blog.csdn.net/weixin_30284355/article/details/96262580)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [数据库的事务和锁机制(SQL Server)](https://blog.csdn.net/oathevil/article/details/5617113)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值