10. 使用事务

这篇博客探讨了在使用Entity Framework时如何处理事务,包括隐式和显式事务的使用。在隐式事务中,`SaveChanges`方法会自动处理事务,如果操作失败则回滚所有更改。而显式事务允许更细粒度的控制,可以通过`BeginTransaction`手动开始事务,并在需要时选择提交或回滚。示例代码展示了如何在C#中实现这些事务操作,并解释了在不同情况下数据库内容的变化。
摘要由CSDN通过智能技术生成

前面在介绍ADO.NET时,介绍了使用事务编程的内容。每次使用Entity Framework访问数据库时,都涉及事务。可以隐式地使用事务或根据需要使用配置显示地创建它们。此节使用的示例项目以两种方式展示事务。这里,Menu、MenuCard和MenuContext类的用法与前面的MenusSample项目相同。

1. 使用隐式的事务

SaveChanges方法的调用会自动解析为一个事务。如果需要进行的一部分变更失败,例如,因为数据库约束,就回滚所有已经完成的更改。下面的代码片段演示了这一点。其中,第一个Menu(m1)用有效的数据创建。对现有MenuCard的引用是通过提供MenuCardId完成的。更新成功后,Menu(m1)的MenuCard属性自动填充。然而,所创建的第二个菜单mInvalid,因为提供的MenuCardId高于数据库中可用的最高ID,所以引用了无效的菜单卡。因为MenuCard和Menu之间定义了外键关系,所以添加这个对象会失败:

        private static async Task AddTwoRecordsWithOneTx()
        {
            Console.WriteLine(nameof(AddTwoRecordsWithOneTx));
            try
            {
                using (var context = new MenusContext())
                {
                    var card = await context.MenuCards.FirstAsync();
                    var m1 = new Menu()
                    {
                        MenuCardId = card.MenuCardId,
                        Text = "added",
                        Price = 99.99m,
                    };
                    int hightestCardId = await context.MenuCards.MaxAsync(c => c.MenuCardId);
                    var mInvalid = new Menu()
                    {
                        MenuCardId = ++hightestCardId,
                        Text = "invalid",
                        Price = 999.99m
                    };
                    await context.Menus.AddRangeAsync(m1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我已经记录下来了你的问题。下面是每个问题的答案: 1. 编写事务实例,分别使其成功提交和回滚,观察该事务对数据库的影响。 在数据库中,事务是指一组操作,它们被视为一个单独的工作单元,要么全部执行成功,要么全部回滚。为了演示事务的影响,我们可以创建一个银行账户表,然后插入一些数据,如下所示: ``` CREATE TABLE bank_account ( id INT PRIMARY KEY, name VARCHAR(50) NOT NULL, balance INT NOT NULL ); INSERT INTO bank_account (id, name, balance) VALUES (1, 'Alice', 1000); INSERT INTO bank_account (id, name, balance) VALUES (2, 'Bob', 2000); ``` 接下来,我们可以编写两个事务,一个是成功提交的,一个是回滚的,如下所示: ``` -- 成功提交的事务 BEGIN TRANSACTION; UPDATE bank_account SET balance = balance - 500 WHERE name = 'Alice'; UPDATE bank_account SET balance = balance + 500 WHERE name = 'Bob'; COMMIT; -- 回滚的事务 BEGIN TRANSACTION; UPDATE bank_account SET balance = balance - 500 WHERE name = 'Alice'; UPDATE bank_account SET balance = balance + 500 WHERE name = 'Bob'; ROLLBACK; ``` 可以看到,第一个事务成功提交了,而第二个事务回滚了。当我们查询银行账户表时,可以看到第一个事务所做的更改已经生效,而第二个事务所做的更改已经被撤销了。 2. 利用数据库管理系统提供的封锁机制解决并发操作带来的数据不一致情况。 在数据库中,封锁机制是一种重要的并发控制机制,它可以防止多个事务同时对同一数据进行修改,从而保证数据的一致性。在 SQL Server 中,可以使用以下语句进行封锁: ``` SELECT * FROM bank_account WITH (TABLOCKX); ``` 这个语句将对 bank_account 表进行排他性锁定,从而防止其他事务对该表进行修改。排他性锁定意味着只有一个事务可以对该表进行修改,其他事务必须等待该事务完成后才能进行修改。 3. 编写事务实例,模拟两个事务产生死锁的情景。 死锁是指两个或多个事务互相持有对方需要的资源,从而导致它们都无法继续执行的情况。为了模拟死锁,我们可以创建一个银行账户表,并且在两个事务中分别对该表进行修改,如下所示: ``` CREATE TABLE bank_account ( id INT PRIMARY KEY, name VARCHAR(50) NOT NULL, balance INT NOT NULL ); INSERT INTO bank_account (id, name, balance) VALUES (1, 'Alice', 1000); INSERT INTO bank_account (id, name, balance) VALUES (2, 'Bob', 2000); ``` 然后,我们可以编写两个事务,一个先修改 Alice 的余额,再修改 Bob 的余额,另一个则反过来。这样就有可能会产生死锁,如下所示: ``` -- 事务1 BEGIN TRANSACTION; UPDATE bank_account SET balance = balance - 500 WHERE name = 'Alice'; WAITFOR DELAY '00:00:10'; UPDATE bank_account SET balance = balance + 500 WHERE name = 'Bob'; COMMIT; -- 事务2 BEGIN TRANSACTION; UPDATE bank_account SET balance = balance - 500 WHERE name = 'Bob'; WAITFOR DELAY '00:00:10'; UPDATE bank_account SET balance = balance + 500 WHERE name = 'Alice'; COMMIT; ``` 可以看到,事务1首先对 Alice 进行了修改,然后等待了一段时间。这时,事务2开始对 Bob 进行修改,也等待了一段时间。这种情况下,可能会发生死锁,因为事务1需要 Bob 的余额才能提交,而事务2需要 Alice 的余额才能提交。如果两个事务同时等待对方完成操作,就会产生死锁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值