【C#】简单认识TransactionScope,以及常见的事务类型

在实际项目开发时,后端编码少不了事务处理。
为什么要用事务,其中一个最直接的原因就是保持数据完整性和一致性

1、C#事务概念

1.1、逻辑单元

1)在C#中,事务是一组操作的逻辑单元,这些操作可以在一个单独的批处理中执行。
2)使用事务可以确保在操作期间的任何时候发生故障时,所有事务中的操作都将回滚(撤销),从而保持数据的一致性和完整性。
3)使用事务还可以确保在并发访问时,对数据的访问是原子性的,即事务中所有操作要么全部成功,要么全部失败。

1.2、Transaction对象

在C#中,可以使用ADO.NET中的事务来实现事务处理。可以通过定义一个Transaction对象,将多个操作包含在一个事务中。在事务中,执行的每个操作都需要使用Transaction对象的方法来指定操作的范围,这样当事务提交或者回滚时,所有操作都会被同时提交或者回滚。
它支持跨库事务。

1.3、事务简单实例

1)创建表

create table myTable(
	name nvarchar(50), 
	email nvarchar(50)
)

2)事务测试

using System.Data.SqlClient;

// 链接数据库
SqlConnection connection = new SqlConnection("Data Source=服务器IP.com;Initial Catalog=数据库名;User ID=账号;Password=密码");
connection.Open();

// 开始事务
SqlTransaction transaction = connection.BeginTransaction();

try
{
    // 执行数据库操作,并将它们添加到事务中
    SqlCommand command1 = new SqlCommand("INSERT INTO myTable (name, email) VALUES ('张三', 'zhangsan@test.com')", connection, transaction);
    SqlCommand command2 = new SqlCommand("UPDATE myTable SET email = 'zhang.san@test.com' WHERE name = '张三'", connection, transaction);
    command1.ExecuteNonQuery();
    command2.ExecuteNonQuery();

    // 事务提交
    transaction.Commit();
}
catch (Exception ex)
{
    // 发生异常,事务回滚
    transaction.Rollback();
}
finally
{
    // 关闭数据库链接
    connection.Close();
}

2、事务场景描述

假设有这样一个场景,在一个请求的接口方法里,进行A表添加操作,同时进行B表更新操作。
可以直观想象下,在没有使用事务情况下,A表添加操作成功了,B表更新失败,这个时候A表和B表就不能保持完整和一致,A表就会多了一条冗余数据。

1)创建表

create table table1 (
	id int, 
	name nvarchar(50)
)

create table table2 (
	id int, 
	name nvarchar(50)
)

1)事务代码

using (var scope = new TransactionScope())
{
    // 在Database1中执行事务操作
    using (var db1 = new SqlConnection("connection_string_1"))
    {
        db1.Open();
        using (var transaction = db1.BeginTransaction())
        {
            // 执行SQL命令
            var command1 = new SqlCommand("INSERT INTO table1 (id, name) VALUES (1, 'test')", db1, transaction);
            command1.ExecuteNonQuery();

            transaction.Commit();
        }
    }

    // 在Database2中执行事务操作
    using (var db2 = new SqlConnection("connection_string_2"))
    {
        db2.Open();
        using (var transaction = db2.BeginTransaction())
        {
            // 执行SQL命令
            var command2 = new SqlCommand("INSERT INTO table2 (id, name) VALUES (2, 'test')", db2, transaction);
            command2.ExecuteNonQuery();

            transaction.Commit();
        }
    }

    // 提交分布式事务
    scope.Complete();
}

3、事务流程图

3.1、没有事务流程

1)页面发起API请求
2)API执行内容逻辑代码
3)执行A表添加,不成功则响应信息回页面
4)A表添加成功,则继续执行下一步代码逻辑
5)执行B表更新,不成功则响应信息回页面
此时,A表记录添加成功,B表更新失败,数据则无法完整和一致
6)B表更新成功,则响应成功信息回页面
7)如果不是重要业务流程,影响不大
8)如果是支付流程,则影响就非常大了
假如支付成功,用户已经付款了,但是支付状态还是未支付状态,这个业务影响就大了
在这里插入图片描述

3.2、有事务流程

1)页面发起API请求
2)API执行内容逻辑代码
3)执行A表添加,不成功则响应信息回页面,回滚事务
4)A表添加成功,则继续执行下一步代码逻辑
5)执行B表更新,不成功则响应信息回页面,回滚事务
此时,事务已经回滚,A表记录添加成功则撤销,A和B表操作,要么都成功,要么都失败
6)B表更新成功,则响应成功信息回页面
此时A和B都执行成功,就可以提交事务,完成两次成功操作
在这里插入图片描述

4、常见事务类型

以下列举C#中常见的事务类型,对于其他开发语言应该也差不多

编号事务类型描述
1本地事务本地事务是一个在单个数据库上运行的事务。该事务的提交或回滚仅对该数据库有效。
2分布式事务分布式事务是一个跨多个数据库或系统的事务。该事务涉及多个资源管理器,例如数据库、消息队列或应用服务器。分布式事务可以是两阶段提交(2PC)或三阶段提交(3PC)。
3隐式事务隐式事务是自动管理的事务,由某些API或框架创建和控制。这些事务的创建和提交通常是透明的,并且不需要明确的代码编写。
4显式事务显式事务是由应用程序开发人员明确创建和控制的事务。显式事务需要显式的开启、提交或回滚操作。
5悲观事务悲观事务认为在任何时间点都会有竞争条件发生。因此,它锁定整个资源并在整个操作期间保持锁定状态。这可以确保只有一个事务可以修改资源。
6乐观事务乐观事务认为在大多数情况下,竞争条件不会发生。因此,它不锁定资源,并且只在提交时检查是否存在竞争条件。如果检测到竞争条件,则事务将回滚。

这些是一些常见的C#事务类型。开发小伙伴应确保选择适当的事务类型以确保数据的一致性和完整性。

  • 29
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 17
    评论
下面是一个简单的使用TransactionScope的代码示例,用于演示如何将多个数据库操作包装在一个事务中: ``` using System; using System.Data.SqlClient; using System.Transactions; class Program { static void Main() { // 创建一个连接字符串 string connectionString = "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=TestDB;Integrated Security=True"; // 创建一个事务范围对象 using (TransactionScope scope = new TransactionScope()) { try { // 执行第一个数据库操作 using (SqlConnection connection1 = new SqlConnection(connectionString)) { connection1.Open(); SqlCommand command1 = new SqlCommand("INSERT INTO Customers (Name) VALUES ('Customer 1')", connection1); command1.ExecuteNonQuery(); } // 执行第二个数据库操作 using (SqlConnection connection2 = new SqlConnection(connectionString)) { connection2.Open(); SqlCommand command2 = new SqlCommand("INSERT INTO Orders (CustomerId, OrderDate) VALUES ((SELECT TOP 1 Id FROM Customers ORDER BY Id DESC), GETDATE())", connection2); command2.ExecuteNonQuery(); } // 提交事务 scope.Complete(); } catch (Exception ex) { // 回滚事务 Console.WriteLine("Transaction rolled back: " + ex.Message); } } } } ``` 在这个示例中,我们使用了TransactionScope类来创建一个事务范围,然后在这个范围内执行了两个数据库操作。如果两个操作都成功执行,我们调用了TransactionScope的Complete方法来提交事务。如果其中任何一个操作失败,我们就会在catch块中回滚事务

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈小5

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值