TransactionScope应用

.NET Framework 2.0 版中新增的 TransactionScope 单独使用确实很方便。但是在实际项目中都有自己的访问层,如何才能和自己的数据访问层结合起来使用呢?
           在项目中我是这样处理数据的:


  /**//// <summary>
    /// 外包业务访问类
    /// </summary>
    public class OutSourcingDAO
    {

        /**//// <summary>
        /// 增加
        /// </summary>
        /// <param name="bt"></param>
        /// <returns></returns>
        public int InsertGetIdentity(OutSourcing bt)
        {
            return new DAHelper(DataAccess.Create()).InsertGetIdentity(bt);
        }
        /**//// <summary>
        /// 更新
        /// </summary>
        /// <param name="bt"></param>
        /// <returns></returns>
        public int Update(OutSourcing bt)
        {
            return new DAHelper(DataAccess.Create()).Update(bt);
        }
        /**//// <summary>
        /// 删除
        /// </summary>
        /// <param name="bt"></param>
        /// <returns></returns>
        public int Delete(OutSourcing bt)
        {
            return new DAHelper(DataAccess.Create()).Delete(bt);
        }
    }
上面 OutSourcing 为与表相对应的实体类 ;DAHelper 为作者自己实现的一个包装类,可以对任意实体进行增加,修改,删除 查询等功能。
  再贴一段代码:

   public int ExecSql(string strSql)
        {
            try
            {
                this.OpenConnection();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = strSql;
             
               return cmd.ExecuteNonQuery();
            }
            catch (System.Exception e)
            {
                throw this.CatchException(e);
            }
            finally
            {
                this.CloseConnection();
            }
        }
我最终通过调用 ExecSql  方法来与数据库交互,而该方法会自己打开数据库连接,执行语句,然后关闭连接。
在操作同一个数据库的时候,如果要高效的使用 TransactionScope,必须保证 SqlConnection 不改变,即用同一个 SqlConnection 来完成所需要的增加
删除,或修改。我想写这样的代码进行事务控制:


 public int UpdateTest(OutSourcing outSourcing, BusinessAccept businessAccept)
        {
            IDataAccess dac = DataAccess.Create();
            DAHelper myHelper = new DAHelper(dac);
            using (TransactionScope ts = new TransactionScope())
            {
                myHelper.Update(outSourcing);
                myHelper.Update(businessAccept);
               
                ts.Complete();
            }
        }
这样就需要,执行第一个操作时候打开数据库连接,执行,不关闭连接,然后执行第二个操作,执行完关闭。显然,我想让 TransactionScope 在
实行 Dispose() 方法的时候关闭数据库连接。using 代码块的本质上等同于 try{}finally{} 语句代码块。为什么不封装一下 TransactionScope 让它满足自己的要求呢?


/**//// <summary>
/// TransactionScope 包装类
/// </summary>
public sealed class Scope : IDisposable
{
    private TransactionScope m_TransactionScope = null;
    /**//// <summary>
    ///  测试访问类
    /// </summary>
    private DataAccessTest m_DataAccessTest = null;
    /**//// <summary>
    /// 实例化一个新的 TransactionScope
    /// </summary>
    /// <param name="dac"></param>
    public Scope(DataAccessTest dac)
    {
        this.m_DataAccessTest = dac;
        //告诉访问类 你已经使用了事务
        dac.SetScope(this);
        this.m_TransactionScope = new TransactionScope();

    }
    /**//// <summary>
    /// 发出事务结束命令
    /// </summary>
    public void Complete()
    {
        this.m_TransactionScope.Complete();
    }
    IDisposable 成员#region IDisposable 成员
    /**//// <summary>
    /// 当执行该方法的时候完成两件任务
    /// 1 关闭数据库连接
    /// 2 调用 TransactionScope 的 Dispose()方法
    /// </summary>
    void IDisposable.Dispose()
    {
        try
        {
            m_DataAccessTest.Close();
        }
        finally
        {
            m_TransactionScope.Dispose();
        }
    }

    #endregion
}


数据库访问类代码如下:


/**//// <summary>
/// 模拟数据库访问类
/// </summary>
public class DataAccessTest
{
    SqlConnection con = null;
    SqlCommand cmd = new SqlCommand();
    Scope scope = null;
    string strCon = "这里是数据库连接字符串。。。。。";

    public void SetScope(Scope scope)
    {
        this.scope = scope;
    }
    private void OpenConnection()
    {

        if (con == null || scope == null)
        {
            con = new SqlConnection(strCon);
            cmd.Connection = con;
            con.Open();
            Console.WriteLine(" 打开数据库连接;");
        }
    }

    private void CloseConnection()
    {
        this.cmd.Parameters.Clear();
        if (scope == null)
        {
            //
            con.Close();
            con.Dispose();
            Console.WriteLine(" 未使用事务 关闭数据库连接;");
        }
    }

    public int ExecuteSql(string strSql)
    {
        try
        {
            this.OpenConnection();
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = strSql;
            Console.WriteLine("执行 Sql 语句。。。");

            return cmd.ExecuteNonQuery();
        }
        catch (System.Exception e)
        {
            throw e;
        }
        finally
        {
            this.CloseConnection();
        }
    }
 
    public void Close()
    {
        con.Close();
        con.Dispose();
        Console.WriteLine(" 关闭数据库连接->该方法由 Scope中的Dispose()方法调用 ");
    }
}
赶快写个方法测试一下吧!


   /**//// <summary>
        /// 测试
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                Console.WriteLine("下面是使用事务的输出。。。。。。。。");
                this.TestACT();
                Console.WriteLine("*********************下面是  未  使用事务的输出。。。。。。。。");
                this.TestNoACT();
            }
            catch (System.Exception ex)
            {
                Console.WriteLine("出现了异常????????????");
                MessageBox.Show(ex.ToString());
            }

        }
        /**//// <summary>
        /// 使用事务
        /// </summary>
        public void TestACT()
        {
            DataAccessTest dac = new DataAccessTest();
            using (Scope scope = new Scope(dac))
            {
                string strSql1 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('a')";
                string strSql2 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('b')";
                dac.ExecuteSql(strSql1);
                dac.ExecuteSql(strSql2);

                scope.Complete();
            }
        }
        /**//// <summary>
        /// 不使用事务
        /// </summary>
        public void TestNoACT()
        {
                DataAccessTest dac = new DataAccessTest();
        
                string strSql1 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('a')";
                string strSql2 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('b')";
                dac.ExecuteSql(strSql1);
                dac.ExecuteSql(strSql2);
        }

 

查看一下输入的结果
下面是使用事务的输出。。。。。。。。
 打开数据库连接;
执行 Sql 语句。。。
执行 Sql 语句。。。
 关闭数据库连接->该方法由 Scope中的Dispose()方法调用
*********************下面是  未  使用事务的输出。。。。。。。。
 打开数据库连接;
执行 Sql 语句。。。
 未使用事务 关闭数据库连接;
 打开数据库连接;
执行 Sql 语句。。。
 未使用事务 关闭数据库连接;

输出结果和想像中的完全一样 数据库也正确(没任何原因不正确!)
最后 改变一下方法:


   /**//// <summary>
        /// 使用事务
        /// </summary>
        public void TestACT()
        {
            DataAccessTest dac = new DataAccessTest();
            using (Scope scope = new Scope(dac))
            {
                string strSql1 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('a')";
                string strSql2 = "INSERT INTO [tilva20].[dbo].[T_Test]([TestName])VALUES('b22222222222222222222222222222222222222222222222222222222222222222222222222222222222222')";
                dac.ExecuteSql(strSql1);
                dac.ExecuteSql(strSql2);

                scope.Complete();
            }
        }
熟悉数据库的肯定知道 “将截断字符串或二进制数据”吧!
输出结果为:
 下面是使用事务的输出。。。。。。。。
 打开数据库连接;
执行 Sql 语句。。。
执行 Sql 语句。。。
 关闭数据库连接->该方法由 Scope中的Dispose()方法调用
出现了异常????????????
数据库一条记录没有增加 数据库连接已经关闭  测试结果完全满意!
对自己访问层稍做修改:
最终的代码为:


   public int UpdateTest(OutSourcing outSourcing, BusinessAccept businessAccept)
        {
            IDataAccess dac = DataAccess.Create();
            DAHelper myHelper = new DAHelper(dac);
            using (Scope ts=new Scope())
            {
                myHelper.Update(outSourcing);
                myHelper.Update(businessAccept);
               
                ts.Complete();
            }
        }

本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/logicbiz/archive/2008/04/23/2317712.aspx

<think>我们正在讨论TransactionScope的使用指南。根据用户需求,我们需要提供TransactionScope的基本使用方法。根据引用[1],我们了解到补偿活动是事务处理中的一个重要概念,而TransactionScope提供了一种声明式的事务管理方式。 TransactionScope是.NET中用于简化事务编程的类,它允许我们将一系列操作包含在一个事务中。如果事务范围内所有操作都成功完成,则提交事务;如果出现异常,则事务会自动回滚。 基本使用步骤: 1. 创建TransactionScope实例 2. 在try块中执行数据库操作等(注意:需要支持分布式事务的资源管理器,如SQL Server) 3. 如果所有操作成功,调用TransactionScope的Complete()方法表示事务完成 4. 在catch块中处理异常(通常不需要显式回滚,因为未调用Complete()时事务会自动回滚) 5. 在finally块中释放TransactionScope(或者使用using语句自动释放) 示例代码(参考引用[1]的代码片段,但需要修正): 引用[1]中的代码示例有一个错误:在catch块中调用了transactionScope.Dispose(),但此时事务范围还没有被释放(因为未调用Complete(),所以事务会回滚)。实际上,我们可以使用using语句来确保范围被正确释放。 正确示例: ```csharp using (var scope = new TransactionScope()) { try { // 执行数据库操作1 // 执行数据库操作2 // ... 其他操作 // 如果所有操作都成功,则标记事务为完成 scope.Complete(); } catch (Exception ex) { // 处理异常,事务会自动回滚(因为未调用Complete()) // 这里可以选择记录日志或抛出异常 throw; // 注意:重新抛出异常时,建议使用throw而不是throw ex,以保留原始堆栈信息 } } ``` 注意:上面的代码中,catch块中的throw语句重新抛出异常,但使用了throw而不是throw ex,这样可以保留原始的异常堆栈信息。另外,由于使用了using语句,无论是否发生异常,都会在最后调用scope.Dispose(),在未调用Complete()的情况下,Dispose()会触发事务回滚。 更简洁的写法(省略catch,让异常向上传播,并在using结束时自动回滚): ```csharp using (var scope = new TransactionScope()) { // 执行一系列操作 // ... // 如果没有异常,则完成事务 scope.Complete(); } ``` 这样,如果操作中抛出异常,则scope.Complete()不会被执行,事务会在using块结束时自动回滚。 重要提示: - 默认情况下,TransactionScope会使用分布式事务(如果涉及多个资源,例如两个不同的数据库)。如果只涉及一个数据库连接,则不会提升为分布式事务。 - 需要确保所有资源管理器(如SQL Server)都支持并配置了分布式事务协调器(MSDTC)以便在需要时使用分布式事务。 使用TransactionScope时,需要引用System.Transactions命名空间(在.NET Framework中,需要添加对System.Transactions.dll的引用;在.NET Core中,需要安装System.Transactions.TransactionScope包)。 现在,我们根据用户的问题,提供TransactionScope的基本使用指南。同时,根据系统指令,我们需要生成相关问题。 §§相关问题§§ 1. TransactionScope在什么情况下会升级为分布式事务? 2. 如何配置TransactionScope的隔离级别? 3. 使用TransactionScope时需要注意哪些常见问题? 4. 在异步代码中如何使用TransactionScope?</think>### TransactionScope 使用指南 TransactionScope 是 .NET 中用于简化事务管理的类(位于 `System.Transactions` 命名空间),它提供了一种声明式的事务管理方式。以下是基本使用方法和关键要点: #### 1. **基本结构** ```csharp using (var scope = new TransactionScope()) { try { // 执行数据库操作(如 ADO.NET/SQL) using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); // 执行多个 SQL 命令... } // 执行其他支持事务的操作(如文件系统、消息队列等) scope.Complete(); // 标记事务成功 } catch (Exception) { // 异常时自动回滚(无需显式调用) throw; } } // 此处自动释放资源,若未调用 Complete() 则回滚事务 ``` #### 2. **核心机制** - **自动提交**:当执行到 `scope.Complete()` 时,事务标记为成功。 - **自动回滚**: - 未调用 `Complete()` 时,离开 `using` 块会自动回滚[^1]。 - 操作中抛出异常也会触发回滚。 - **嵌套事务**:支持嵌套作用域,子事务会加入父事务(默认 `TransactionScopeOption.Required`)。 #### 3. **关键配置** ```csharp // 设置隔离级别和超时时间 var options = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted, // 默认值 Timeout = TimeSpan.FromSeconds(30) // 默认 60 秒 }; using (var scope = new TransactionScope( TransactionScopeOption.Required, options )) { // ... } ``` #### 4. **重要注意事项** 1. **资源管理器支持**: - 需使用支持事务的资源(如 SQL Server、MSMQ)。 - 跨多个资源管理器时(如两个数据库),会升级为分布式事务(需启用 MSDTC 服务)。 2. **异步操作**: ```csharp using (var scope = new TransactionScope( TransactionScopeAsyncFlowOption.Enabled )) { await DatabaseOperationAsync(); scope.Complete(); } ``` 3. **避免长时间事务**: - 设置合理的 `Timeout` 值。 - 在事务内避免耗时操作(如 HTTP 请求)。 #### 5. **典型应用场景** - 跨多个数据库的原子操作 - 数据库操作与文件系统操作的协调 - 需要自动回滚补偿的业务流程 > 示例参考:补偿活动则是为事务中已经成功执行的操作提供了一种“撤销”机制。如果事务未能成功完成,补偿活动会自动执行,以恢复到事务之前的状态[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值