TransactionScope事务处理方法介绍及.NET Core中的注意事项
作者:依乐祝
今天在写CzarCms的UnitOfWork的使用使用到了这个TransactionScope事务,因此对它进行了相关资料的查阅并记录如下,希望对大伙在.NET Core中使用有所帮助。
写在前面
您是否曾尝试使用C#代码来实现事务?通常,我们在SQL中一次执行多个Insert / Update语句的话可能就会使用到事务。事务遵循ACID(原子性,一致性,隔离性,持久性)规则,这样所有的语句要么全部执行成功要么全部被取消并执行回滚操作。 而我们今天要讲的TransactionScope则可以允许我们在应用程序级别实现这个过程。在某些情况下,您可能需要在同一个数据库甚至多个数据库(分布式事务)中执行不同的操作,或者由于某些其他约束,它无法在数据库级别来完成,或者应用程序的开发人员对数据库的接触较少,那么这时候TransactionScope将会让你游刃有余。
什么是TransactionScope呢?
TransactionScope作为System.Transactions的一部分被引入到.NET 2.0。同时SqlClient for .NET Core 从 2.1 及以上版本开始提供对System.Transactions的支持 。 它是一个类,它提供了一种简单的方法,可以将一组操作作为事务的一部分来进行处理,而不必担心场景背后的复杂性。如果某个操作在执行的过程中失败的话,则整个事务将失败并执行回滚操作,从而撤消已完成的所有操作。所有这些都将由框架处理,从而确保数据的一致性。
如何使用TransactionScope呢?
要使用它,您需要添加System.Transactions的引用,如果你使用的是.net core的话。这个引用被包含在netcoreapp2.2\System.Transactions.Local.dll 中, 该引用是框架库的一部分(通常默认情况下不会自动添加)。添加后,在我们想要使用它的地方添加名称空间 System.Transactions即可。代码如下所示:
try
{
using (TransactionScope scope = new TransactionScope())
{
// Do Operation 1
// Do Operation 2
//...
// 如果所有的操作都执行成功,则Complete()会被调用来提交事务
// 如果发生异常,则不会调用它并回滚事务
scope.Complete();
}
}
catch (ThreadAbortException ex)
{
// 处理异常
}
在上面的代码中我们可以看到我们在创建TransactionScope实例时使用了using 语句块及Disposable块,它确保了当dispose离开块并结束事务范围时调用dispose来进行资源的释放。
在一个Transaction范围中,我们可以做多个连接甚至链接到不同数据库的操作的,如下所示:
using (TransactionScope scope = new TransactionScope())
{
using (con = new SqlConnection(conString1))
{
con.Open();
// 执行操作 1
// 执行操作 2
//...
}
using (con = new SqlConnection(conString2))
{
con.Open();
// 执行操作 1
// 执行操作 2
//...
}
scope.Complete();
}
下面我们使用两个不同的数据库连接字符串来连接不同的数据库。当然我们也可以根据我们的业务要求使用尽可能多数据库。我们也可以再事务中嵌套事务。如下代码所示:
public void DoMultipleTransaction()
{
try
{
using (TransactionScope scope = new TransactionScope())
{
using (con = new SqlConnection(conString1))
{
con.Open();
// 执行操作1
}
OtherTransaction();
scope.Complete();
}
}
catch (ThreadAbortException ex)
{
// 处理异常
}
}
private void OtherTransaction()
{
using (TransactionScope scope = new TransactionScope())
{
using (con = new SqlConnection(conString2))
{
con.Open();
// 执行操作
}
scope.Complete();
}
}
这里最顶层的事务范围称为根范围。另外这里需要注意的是即使通过调用scope.Complete()完成内部事务(上面的OtherTransaction ),如果由于各种原因无法调用rootscope complete,那么整个事务也将被回滚包括内部的事务。
*注意:执行分布式trsanctions时,您可能会收到以下异常之一*
- 服务器上的MSDTC不可用
- 已禁用分布式事务管理器(MSDTC)的网络访问。
这两个错误都是由于同样的原因,第一个是在数据库和应用程序是同一个服务器时发生的,而在另一个则是服务跟数据库分别部署在两台服务器上。对于同一台服务器,请转到run-> cmd-> services.msc。运行名为Distributed Transaction Coordinator的服务并自动启动启动类型,以便在系统重新启动时再次启动它。对于2,你可能需要参照这个链接的内容进行相应的设置
TransactionScope 类提供了多个重载构造函数,它们接受 TransactionScopeOption 类型的枚举,而该枚举定义事务范围行为。
TransactionScope对象有以下三个选项:
- Required:联接环境事务,或者在环境事务不存在的情况下创建新的环境事务。
- RequiresNew:成为新的根范围,也就是说,启动一个新事务并使该事务成为其自己范围中的新环境事务。
- Suppress:根本不参与事务。 因此没有环境事务。
如果用 Required] 实例化范围并且存在环境事务,则该范围会联接该事务。 相反,如果不存在环境事务,该范围就会创建新的事务并成为根范围。 这是默认值。 在使用 Required时,无论范围是根范围还是仅联接环境事务,该范围中的代码都不需要有不同的行为。 该代码在这两种情况下的行为应相同。
如果用 RequiresNew 实例化范围,则它始终为根范围。 它会启动一个新事务,并且其事务成为该范围中的新环境事务。
如果用 Suppress 实例化范围,则无论是否存在环境事务,范围都从不参与事务。 始终使用此值实例化的作用域具有null作为其环境事务。
下面来让我们看一组实例代码:
using (TransactionScope scope = new TransactionScope())
{
// 联接环境事务,或者在环境事务不存在的情况下创建新的环境事务。
using (TransactionScope scope1 = new TransactionScope(TransactionScopeOption.Required))
{
// Do Operation
scope1.Complete();
}
//成为新的根范围,也就是说,启动一个新事务并使该事务成为其自己范围中的新环境事务。
using (TransactionScope scope2 = new TransactionScope(TransactionScopeOption.RequiresNew))
{
// Do Operation
scope2.Complete();
}
//根本不参与事务。 因此没有环境事务。
using (TransactionScope scope3 = new TransactionScope(TransactionScopeOption.Suppress))
{
// Do Operation
scope3.Complete();
}
scope.Complet
在这里,我们使用不同的TransactionScopeOptions在父事务下创建了三个事务。默认情况下,范围是required ,这里父事务就是采用的这个默认参数进行创建的。它是一个创建新事务的根范围,并将其标记为环境事务。scope1也是使用required创建的,因为我们已经有了一个环境事务(范围),所以它加入到父事务中。scope2是使用RequiresNew选项创建的,这意味着它是一个独立于环境事务处理的新事务。scope3是用suppress创建的选项,这意味着它不参与任何环境事务。无论环境事务是否成功执行,它都会被执行。父(全局)范围完成后,将提交所有环境事务。
注意点
EF Core 依赖数据库提供程序以实现对 System.Transactions 的支持。 虽然支持在 .NET Framework 的 ADO.NET 提供程序之间十分常见,但最近才将 API 添加到 .NET Core,因此支持并未得到广泛应用。 如果提供程序未实现对 System.Transactions 的支持,则可能会完全忽略对这些 API 的调用。 SqlClient for .NET Core 从 2.1 及以上版本开始支持 System.Transactions。如果尝试在低版本中 如.NET Core 2.0中尝试使用该功能将引发异常。
自版本 2.1 起,.NET Core 中的 System.Transactions 实现将不包括对分布式事务的支持,因此不能使用
TransactionScope或CommittableTransaction来跨多个资源管理器协调事务。主要是不依赖windows中的mstsc功能。异步方法使用时需要注意:
在下面的例子中,我们在
TransactionScope内部使用await。using(var scope = new TransactionScope())
{
var groups = await Context.ProductGroups.ToListAsync()。ConfigureAwait(false);
}
看起来没有问题,但它会抛出一个
System.InvalidOperationException:``A TransactionScope must be disposed on the same thread that it was created.原因是默认情况下
TransactionScope不会从一个线程切换到另一个线程。为了解决这个问题,我们必须使用TransactionScopeAsyncFlowOption.Enabled:using(var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
var groups = await Context.ProductGroups.ToListAsync()。ConfigureAwait(false);
}
现在应该可以了吧?这取决于下面的情况。
如果我们使用和不使用
TransactionScopeAsyncFlowOption这个选项的时候都使用了相同的数据库连接,并且第一次执行的时候没有使用这个选项,那么我们会得到另一个异常:System.InvalidOperationException:``Connection currently has transaction enlisted. Finish current transaction and retry.换句话说,由于第一个访问的原因,第二个会话将会失败。如下代码所示:
try
{
using (var scope = new TransactionScope())
{
// We know this one - System.InvalidOperationException:
// TransactionScope必须放在与创建它相同的线程上。
var groups = await Context.ProductGroups.ToListAsync().ConfigureAwait(false);
}
}
catch (Exception e)
{
// error handling
} using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
// Implemented correctly but throws anyways
// System.InvalidOperationException:
// 当前连接已经被记录。完成当前事务并重试。
var groups = await Context.ProductGroups.ToListAsync().ConfigureAwait(false);
}
想象一下,如果第一个调用是在第三方库或您正在使用的框架中完成的,二您不了解其中的代码 - 如果您之前没有看到此错误,那么你讲无从下手来解决这个问题。
总结
本文带着大家熟悉了一遍TransactionScope并对其使用进行了介绍!同时介绍了在.NET Core中使用TransactionScope的一些注意事项!希望对大家有所帮助。另附上.NET Core实战项目交流群:637326624
TransactionScope事务处理方法介绍及.NET Core中的注意事项的更多相关文章
- TransactionScope事务处理方法介绍及.NET Core中的注意事项 SQL Server数据库漏洞评估了解一下 预热ASP.NET MVC 的VIEW [AUTOMAPPER]反射自动注册AUTOMAPPER PROFILE
TransactionScope事务处理方法介绍及.NET Core中的注意事项 作者:依乐祝 原文链接:https://www.cnblogs.com/yilezhu/p/10170712.ht ...
- 【转】TransactionScope事务处理方法介绍及.NET Core中的注意事项
什么是TransactionScope呢? TransactionScope作为System.Transactions的一部分被引入到.NET 2.0.同时SqlClient for .NET Cor ...
- 【翻译】介绍 ASP.NET Core 中的 Razor Pages
介绍 ASP.NET Core 中的 Razor Pages 原文地址:Introduction to Razor Pages in ASP.NET Core 译文地址:介绍 asp. ...
- ORACLE PL/SQL 中序列(sequence)的简易使用方法介绍
如果我是C罗 原文 ORACLE PL/SQL 中序列(sequence)的简易使用方法介绍 sequence在ORACLE中应用十分广泛,就是序列号的意思,会自动增加指定变数,如逐次增加1或者2或者 ...
- [翻译]在 .NET Core 中的并发编程
原文地址:http://www.dotnetcurry.com/dotnet/1360/concurrent-programming-dotnet-core 今天我们购买的每台电脑都有一个多核心的 C ...
- 玩转ASP.NET Core中的日志组件
简介 日志组件,作为程序员使用频率最高的组件,给程序员开发调试程序提供了必要的信息.ASP.NET Core中内置了一个通用日志接口ILogger,并实现了多种内置的日志提供器,例如 Console ...
- 在 .NET Core 中结合 HttpClientFactory 使用 Polly(下篇)
译者:王亮作者:Polly 团队原文:http://t.cn/EhZ90oq声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的(包括标题).其中可能会去除一些不影响理解但本人实在不知道如 ...
- 在 .NET Core 中结合 HttpClientFactory 使用 Polly(中篇)
译者:王亮作者:Polly 团队原文:http://t.cn/EhZ90oq声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的(包括标题).其中可能会去除一些不影响理解但本人实在不知道如 ...
- .NET Core 中的并发编程
今天我们购买的每台电脑都有一个多核心的 CPU,允许它并行执行多个指令.操作系统通过将进程调度到不同的内核来发挥这个结构的优点. 然而,还可以通过异步 I/O 操作和并行处理来帮助我们提高单个应用程序 ...
随机推荐
- pycharm设置文件编码
原文链接
- Unity3D 中 脚本(MonoBehaviour) 生命周期WaitForEndOfFrame需要注意的地方
首先看看MonoBehaviour的生命周期 先上个图(来源 http://blog.csdn.net/qitian67/article/details/18516503): 1.Awake 和 St ...
- js几种数组遍历方法.
第一种:普通的for循环 ; i < arr.length; i++) { } 这是最简单的一种遍历方法,也是使用的最多的一种,但是还能优化. 第二种:优化版for循环 ,len=arr.len ...
- No rule to make target '/usr/lib/x86_64-linux-gnu/libproj.so ,needed by '../bin/generate_pointcloud解决方法
这是/usr/lib/x86_64-linux-gnu/文件夹内没有这个libproj.so 库,先在该文件夹内搜索是否有同名不同版本的库,如果有 ,可以使用ln -s在该文件夹内部对存在的库链接到一 ...
- 安装jdk+tomcat
linux安装高版本jdk后不起作用的解决办法 1.安装jdk1.8. 2.修改/etc/profile中的JAVA_HOME为新的jdk路径,并执行source /etc/profile. 3.执 ...
- 02.02.03第3章 餐饮项目案例(Power BI商业智能分析)
02.02.03第3章 餐饮项目案例 02.02.03.01餐饮数据理解与读入 00:06:12 02.02.03.02餐饮数据处理 00:29:57 处理生成的表为: 02.02.03.03餐饮数据 ...
- Python基础环境搭建
一.编程语言 1.解释型语言:每执行一次程序就要将代码翻译一次,如Java.JavaScript.VBScript.Perl.Python.Ruby.MATLAB 等都是解释型语言 2.编译型语言:程 ...
- Linux如何挂载U盘
1,以root用户登陆 先加载USB模块 modprobe usb-storage 用fdisk -l 看看U盘的设备 假如U盘是sda1 2,确定在 目录 /mnt 下建立了 文件夹 / ...
- 2019.03.25 bzoj4568: [Scoi2016]幸运数字(倍增+线性基)
传送门 题意:给你一棵带点权的树,多次询问路径的最大异或和. 思路: 线性基上树?? 倍增维护一下就完了. 时间复杂度O(nlog3n)O(nlog^3n)O(nlog3n) 代码: #include ...
- PHP与Excel 笔记
一: PHP将数据导出Excel表中(投机型) 二: PHPExcel: Github上可以下载此插件包,用法如下: 前端: //上传阅卷员Excel文件 $("#upload_memb ...