C# 事务

来源:互联网 发布:李白的文学地位知乎 编辑:程序博客网 时间:2024/06/10 06:39

原文链接:http://kb.cnblogs.com/page/127650/

其实事务在数据层、服务层、业务逻辑层多处地方都会使用到。

一、事务的定义

所谓事务,它是一个操作集,这些操作要么都执行,要么都不执行。它是一个不可分割的工作单位。

典型的例子:
网上银行系统的账户A转账到账户B,经过两个阶段:
1、从账户A取出款项
2、把款项放入账户B中
这两个过程要么同时成功,要么同时失败。
这一系列的操作就被称为事务性操作

在一个事务性操作的环境下,操作有着以下的4种特性,被称为ACID特性。

1、原子性(Atomicity): 当事务结束,它对所有资源状态的改变都被视为一个操作,这些操作要不同时成功,要不同时失败。
2、一致性(Consistency): 操作完成后,所有数据必须符合业务规则,否则事务必须中止。
3、隔离性(Isolation): 事务以相互隔离的方式执行,事务以外的实体无法知道事务过程中的中间状态。
4、持久性(Durable): 事务提交后,数据必须以一种持久性方式存储起来。

二、事务管理器

在软件系统当中可以看到无论在数据库、Web服务、WCF、文件系统都存在着数据参与到事务运作当中,我们把管理这些数据的工具称为资源管理器RM(Resources Manager)。而事务管理器TM(Transaction Manager)就是协调多个资源管理器的工作,保证数据完整性的工具。

这里写图片描述

由上图可以看到事务的管理流程,系统通知事务管理器TM来启动事务,事务管理器TM控制向多个资源管理器RM并协调RM之间的事务操作。图中存在两个持久化RM,分别管理数据库和文件系统,这些事务操作要不同时成功,要不同时失败。

事务管理器一般分为三类:
1、轻量级事务管理器(LTM)
2、核心事务管理器(KTM)
3、分布式事务协调器(DTC)。

1、 轻量级事务管理器 (LTM)
  它是包括在 System.Transactions 命名空间内的一个事务管理框架,它只能管理单个应用程序域内的事务。LTM 可以管理多个易变的RM,但只能管理一个持久化RM。若事务试图加入第二个持久化RM,那轻量级事务管理器LTM将提升级别。LTM是性能最高的事务管理器,在可选择的情况下应该尽可能地使用 LTM 事务管理器。
  这里易变RM是指它参与会引发 “未确定状态” 的2PC事务的时候,不需要恢复服务,更多时候,易变RM的数据只存储在内存当中。
  而持久化RM是指它参与会引发 “未确定状态” 的2PC事务的时候,它需要恢复服务,持久化RM管理的数据是在于硬盘当中。所以,参与2PC事务的的持久RM必须有新旧两个版本,如果事务引发 “未确定状态” 的时候,那么它就会联系持久化RM,恢复到其中一个版本。
2PC说明 (http://blogs.msdn.com/b/diegumzone/archive/2006/08/14/699219.aspx)  
  2PC 是2 Phase Commit的缩写,代表事务的2阶段提交验证算法:在数据提交时,第一阶段:应用程序记录每个数据源并执行更新请求,TM通知每个RM来执行分布式事 务,然后每个RM都对数据执行本地的事务,在事务将提交前,TM会与各个RM进行信息交换,以获知更新是否成功。第二阶段,如果其中任何一个RM表示更新 失败,TM就会通知所有的RM事务操作失败,实现数据回滚。如果所有RM的操作都成功,那么整个TM事务就宣告成功。  
2、 核心事务管理器 (KTM)
  KTM是用于Windows Vista和Windows Server 2008 系统中的轻量级事务管理器,与LTM相像,它可以管理多个易变的RM,但只能管理一个持久化RM。
3、 分布式事务协调器(DTC)
  分布式事务协调器DTC(Distributed Transaction Coordinator)能管理多个持久化RM中的事务,事务可以跨越应用程序域、进程、硬件、域等所有的边界。在Windows Server 2008当中,DTC支持OleDB、XA、WS-AtomicTransaction、WSCoordination、WS-BusinessActivity等多个协议。由于分布式事务需要在多个参与方之间实现多次通讯,所以是一种巨大的开销,因此,在可以使用LTM和KTM的时候,应该尽量避免使用DTC。在上面图片中的事务同时启动了两个RM分别处理数据库数据与文件数据,当中启动的就是DTC分布式事务。

 4. 事务类 System.Transactioins.Transaction
 Transaction是由Framework 2.0 就开始引入,用于显示管理事务的一个类。通过Transaction可以直接管理事务的中止、释放,也可以获取、克隆当前的环境事务类。
Transaction的公用属性
  其中Transaction.Current 比较常用,它可以指向一个当前运行环境中的事务,如果环境事务不存在,系统将返回一个null
  Transaction transaction=Transaction.Current;
属性 说明
Current 获取或设置环境事务。
IsolationLevel 获取事务的隔离级别。
TransactionInformation 检索有关某个事务的附加信息。
Transaction的常用公用方法
  其中Rollback、Dispose方法可以控制事务中止、释放,而Clone、DependentClone方法在多线程操作中经常用到,在 “异步事务” 一节中将详细说明
方法 说明
Rollback 中止事务、回滚。
Dispose 释放事务对象。
Clone     创建事务克隆
DependentClone 创建事务的依赖克隆。
Transaction的事件
  在事务完成后,会触发TransactionCompleted事件,开发人员可以在此事件的过程监测其状态
事件 说明
TransactionCompleted 在事务完成后执行
5、事务状态 TransactionInformation
  上面讲解过事务分为本地事务与分布式事务,而Transaction类的TransactionInformation是事务状态的记录,它可以跟踪事务动作,分辨事务现处的状态,记录本地事务与分布式事务的Guid。
  TransactionInformation有两个重要成员

public class TransactionInformation{      //返回分布式事务标识符      public Guid DistributedIdentifier       {get;}      //返回本地事务标识符      public string LocalIdentifier      {get;}}

LocalIndentifier是本地事务的标识符,它可以获取本地事务管理器(LTM)的ID,并且注意只要事务存在,它的值就永远不会是null。它包含两个部分,一个是LTM的Guid,它是应用程序中的唯一值,代表了现存应用程序域分配的LTM。另一部分是一个可变量,代表了当时该应用程序域中的事务数量。
  例如:3427dec9-4abc-34cc-9edf-30ad835c33k3:3
  其中3427dec9-4abc-34cc-9edf-30ad835c33k3是此本地事务管理器的Guid,在事务启动后,此值都是不变的,而 “3” 代表此刻该应用程序域中存在 “3” 个本地事务。
  DistributedIndentifier是分布式事务的标识符,在普通情况下DistributedIndentifier的值都为Guid.Empty。但当LTM或KTM事务被提升到分布式事务时,DistributedIndentifier就会产生。最重的是,在同一个分布式事务管理器当中,即使事务跨越服务边界,分布式ID都是一致的。DistributedIndentifier是分布式事务的唯一标识符,它的使用方法在后面 “事务的传播” 一节将详细介绍。
  在TransactionManager类中,还提供了一个事件DistributedTransactionStarted专门用于测试分布式事务的变化。

class Program{    static void Main(string[] args)    {        using (TransactionScope scope = new TransactionScope())        {            TransactionManager.DistributedTransactionStarted += OnDistributedTransactionStarted;            ............            scope.Complete();        }        Console.ReadKey();    }    //当执行分布式事务是就会启动此方法显示事务信息    static void OnDistributedTransactionStarted(object sender, TransactionEventArgs args)    {        Transaction transaction = args.Transaction;        Console.WriteLine("Distributed Transaction Started!\n  DistributedIndentifier:"             +transaction.TransactionInformation.DistributedIdentifier);    }}

 基础知识就先讲到这里,下面开始介绍一下事务的具体用法。

 三、在ADO.NET中实现事务
 
  1. ADO.NET事务的主要成员
  需要使用事务管理的情况很多,在数据层使用得特别广泛,几乎每一个系统的数据层都会实现事务。数据层的事务都是继承自DBTransaction,派生自IDbTransaction的。下面介绍一下IDbTransaction的基本成员:

public interface IDbTransaction:IDisposable{      IDbConnection Connection {get;}     //返回Connection对象      IsolationLevel IsolationLevel{get;}         void Commit();      //数据提交,把所有改变数据保存到持久化数据库      void Rollback();     //数据回滚,把所有数据恢复原值}

  其中Connection属性是返回初始化此事务时所引用的连接对象的。Commit()方法应该在完成所有数据操作后才调用,调用该方法后,已经改变的数据将会保存到持久化数据库当中。而Rollback()是出现错误时调用的,调用后数据将返回初始值。IsolationLevel是指定遇到其它并行事务时的处理方式。
  ADO.NET当中有多个子类都继续自DBTransaction,其中SqlTransaction是比较常用的,SqlTransaction中还定义了一个Save()方法,这个方法允许开发人员把失败的事务回滚到上一个保存点而不回滚整个事务。而在DataContext类里面,Transaction属性会返回DBTransaction对象。
这里写图片描述

  2. 开发实例
  在传统的ADO.NET中使用事务,方法如下:

private static void Execute(string connectionString){    using (SqlConnection connection = new SqlConnection(connectionString))    {        connection.Open();        SqlCommand command = connection.CreateCommand();        SqlTransaction transaction;        //启动事务        transaction = connection.BeginTransaction("SampleTransaction");        //设定SqlCommand的事务和连接对象        command.Connection = connection;        command.Transaction = transaction;        try        {            command.CommandText ="Insert into ......";            command.ExecuteNonQuery();            // 完成提交            transaction.Commit();            ......        }        catch (Exception ex)        {            //数据回滚            transaction.Rollback();            .....        }    }}

在DataContext中使用事务,方法极其相似,不同的是SqlCommand中事务为SqlTransaction,在DataContext中事务为DbTransaction

using(MyDataContext context=new MyDataContext()){      try      {          context.Connection.Open();          context.Transaction=context.Connection.BeginTransaction();          //更新数据          .........          context.SubmitChanges();          //事务提交          context.Transaction.Commit();      }      catch(Excetion ex)      {         //数据回滚         context.Transaction.Rollback();         //错误处理         .........      }}  

 四、隐式事务 TransactionScope
  1. TransactionScope的概念
  TransactionScope存在于System.Transactions 命名空间中, 它是从Framework 2.0开始引入的一个事务管理类,它也是微软推荐使用的一个事务管理类。在TransactionScope的构造函数中会自动创建了一个新的LTM(轻量级事务管理器),并通过Transaction.Current 隐式把它设置为环境事务。在使用隐式事务时,事务完成前程序应该调用TransactionScope的Complete()方法,把事务提交,最后利用Dispose()释放事务对象。若执行期间出现错误,事务将自动回滚。

public class TransactionScope:IDisposable{    //多个构造函数    public TransactionScope();    public TransactionScope(Transaction)    public TransactionScope(TransactionScopeOption)    ......    public void Complete();   //提交事务    public void Dispose();     //释放事务对象}//调用方式using(TransactionScope scope=new TransactionScope()){     //执行事务型工作     ............     scope.Complete();} 
  1. TransactionScope的构造函数 TransactionScope(transactionScopeOption)
      TransactionScopeOption 是枚举的一个实例,它主要用于TransactionScope的构造函数内,定义事务生成的状态要求。
      在MSDN里面可以找到它的定义:
      http://msdn.microsoft.com/zh-cn/library/system.transactions.transactionscopeoption.aspx  
    成员名称 说明
    Required 该范围需要一个事务。 如果已经存在环境事务,则使用该环境事务。 否则,在进入范围之前创建新的事务。 这是默认值。
    RequiresNew 总是为该范围创建新事务。
    Suppress 环境事务上下文在创建范围时被取消。 范围中的所有操作都在无环境事务上下文的情况下完成。
      这里Suppress有点特别,当使用Suppress范围内,所有的操作都将在无事务的上下文中执行,即当中的程序不再受到事务的保护,这大多数在嵌套式事务中使用。
void DoWork(){    using(TransactionScope scope=new TransactionScope())    {         //在事务环境中执行操作         ......           NoTransaction();         scope.Complete();    }}void NoTransaction(){    //在无事务环境中执行操作    using(TransactionScope scope=new TransactionScope(TransactionScopeOption.Suppress))    {        ......    }} 

  3. 应用实例,在Entity Framework中使用TransactionScope

public Order GetOrder(int id){    BusinessContext _context = new BusinessContext();    Order order = null;    try    {        using (TransactionScope scope = new TransactionScope())        {             //数据操作            var list = _context.Order.Include("OrderItem")                .Where(x => x.ID == id);            if (list.Count() > 0)                order = list.First();            else                order = new Order();            scope.Complete();      //事务提交        }    }    catch (Exception ex)    {        ...... //出错处理,并返回一个空对象        order=new Order();       }    _context.Dispose();    return order;} 
原创粉丝点击