最近项目中遇到一个困惑,在一个事务中,如果A方法执行成功,则执行B方法,并提交事务,如果A方法执行失败,此时,需要回滚事务吗?
为了弄清楚这个问题,我们通过三个C#示例方法进行分析,它们的功能都是删除Category表和Product表中CategoryId为1的记录,但是使用了不同的方式处理事务。代码如下:
using DbContext dbContext = new ProductContext();
void Method1()//方法一
{
using var transaction = dbContext.Database.BeginTransaction();
{
int row = dbContext.Database.ExecuteSql($"DELETE FROM [Category] WHERE [CategoryId]={1}");
if (row > 0)
{
dbContext.Database.ExecuteSql($"DELETE FROM [Product] WHERE [CategoryId]={1}");
transaction.Commit();
}
}
}
void Method2()//方法二
{
using var transaction = dbContext.Database.BeginTransaction();
{
int row = dbContext.Database.ExecuteSql($"DELETE FROM [Category] WHERE [CategoryId]={1}");
if (row > 0)
{
dbContext.Database.ExecuteSql($"DELETE FROM [Product] WHERE [CategoryId]={1}");
}
transaction.Commit();
}
}
void Method3()//方法三
{
using var transaction = dbContext.Database.BeginTransaction();
{
int row = dbContext.Database.ExecuteSql($"DELETE FROM [Category] WHERE [CategoryId]={1}");
if (row > 0)
{
dbContext.Database.ExecuteSql($"DELETE FROM [Product] WHERE [CategoryId]={1}");
transaction.Commit();
}
else
{
transaction.Rollback();
}
}
}
这里我们需要注意一个重要的知识点:
在EF Core中,使用using var transaction = dbContext.Database.BeginTransaction()这样的写法,在using结束时,如果没有手动提交事务,它会自动回滚事务,而不会自动提交事务。因为using语句会在结束时调用transaction对象的Dispose方法,而Dispose方法会检查事务是否已经提交或回滚,如果没有,则会调用Rollback方法回滚。
一、过程分析
接下来,我们来分别分析一下这三个方法的执行过程。点个关注吧👇
1、方法一的执行过程如下:
调用dbContext.Database.BeginTransaction()方法,开始一个数据库事务,返回一个DbTransaction对象,赋值给transaction变量。
调用dbContext.Database.ExecuteSql()方法,删除Category表中CategoryId为1的记录,返回受影响的行数,赋值给row变量。
如果row大于0,则继续删除Product表中CategoryId为1的记录,然后调用transaction.Commit()方法,提交事务,释放transaction对象资源。
否则,通过 using 块结束时自动调用 transaction.Dispose()方法,隐式地回滚事务,释放transaction对象资源。
2、方法二的执行过程如下:
调用dbContext.Database.BeginTransaction()方法,开始一个数据库事务,返回一个DbTransaction对象,赋值给transaction变量。
调用dbContext.Database.ExecuteSql()方法,删除Category表中CategoryId为1的记录,返回受影响的行数,赋值给row变量。
如果row大于0,则继续删除Product表中CategoryId为1的记录,然后调用transaction.Commit()方法,提交事务,释放transaction对象资源。
否则,调用transaction.Commit()方法,提交一个空事务,释放transaction对象资源。
3、方法三的执行过程如下:
调用dbContext.Database.BeginTransaction()方法,开始一个数据库事务,返回一个DbTransaction对象,赋值给transaction变量。
调用dbContext.Database.ExecuteSql()方法,删除Category表中CategoryId为1的记录,返回受影响的行数,赋值给row变量。
如果row大于0,则继续删除Product表中CategoryId为1的记录,然后调用transaction.Commit()方法,提交事务,释放transaction对象资源。
否则,调用transaction.Rollback()方法,回滚事务,释放transaction对象资源。
二、分析结论
通过对三个方法的分析,我们可以得出以下结论:
方法一和方法三的执行结果完全相同,唯一的区别是方法三能及时地释放数据库资源,而方法一则依赖using块结束时来隐式地回滚事务。这样可能会导致数据库的资源被占用较长时间,影响其他操作的执行和性能。
方法二与其它两个方法的主要不同点在于当row小于等于0的时候,方法二提交了一个空事务,这样会浪费数据库的资源和时间,影响性能和并发能力。
日常的开发中推荐使用方法三来处理事务,因为它既能在成功时提交事务,也能在失败时回滚事务,并能及时释放掉数据库资源。
当然,这里讨论的时间长短是相较三个方法而言,使用 using 自动释放资源并无不妥。无论如何,我们在使用事务时要注意正确地提交或回滚事务,并及时释放资源。
参考资料:
https://learn.microsoft.com/en-us/ef/core/saving/transactions
👇感谢阅读,点赞+分享+收藏+关注👇
文章出自猿惑豁微信公众号