TransactionScope的使用--业务层使用多个方法实现回滚解决方案-业务层事务

TransactionScope 分场景使用才可以

  System.Transactions 基础结构既提供了基于 Transaction 类的显式编程模型,也提供了使用 TransactionScope 类的隐式编程模型,在后一种模型中,事务由该基础结构自动管理。

 

重要说明:

建议使用 TransactionScope 类创建隐式事务,以便自动为您管理环境事务上下文。对于需要跨多个函数调用或多个线程调用使用相同事务的应用程序,您还应该使用 TransactionScope 和 DependentTransaction 类。有关此模型的更多信息,请参见使用事务范围实现隐式事务 主题。有关编写事务性应用程序的更多信息,请参见编写事务应用程序。

 

在通过 new 语句实例化 TransactionScope 时,事务管理器将确定要参与哪个事务。一经确定,此范围将始终参与该事务。此决策基于两个因素:是否存在环境事务以及构造函数中 TransactionScopeOption 参数的值。 环境事务是在其中执行您的代码的事务。通过调用 Transaction 类的 Current 静态属性可获取对环境事务的引用。有关如何使用此参数的更多信息,请参见 使用事务范围实现隐式事务 主题的“事务流管理”一节。

如果在事务范围中(即从初始化 TransactionScope 对象到调用其 Dispose 方法之间)未发生异常,则允许该范围所参与的事务继续。如果事务范围中的确发生了异常,它所参与的事务将回滚。

当应用程序完成它要在一个事务中执行的所有工作以后,您应当只调用 Complete 方法一次,以通知事务管理器可以接受提交事务。未能调用此方法将中止该事务。

对 Dispose 方法的调用标志着该事务范围的结束。在调用此方法之后发生的异常不会影响该事务。

如果在范围中修改 Current 的值,则会在调用 Dispose 时引发异常。但是,在该范围结束时,先前的值将被还原。此外,如果在创建事务的事务范围内对 Current 调用 Dispose,则该事务将在相应范围末尾处中止。



一、TransactionScope的优点

1、使用起来比较方便.TransactionScope可以实现隐式的事务,使你可以在写数据访问层代码的时候不用考虑到事务,而在业务层的控制事务. 
2、可以实现分布式事务,比如跨库或MSMQ.

二、TransactionScope缺点 
1、性价比不高.比如,你只是在”Scope”里控制一个库的事务.用”TransactionScope”就有点浪费了. 
2、一般情况下只要你使用”TransactionScope”,都要配置MSDTC,要配防火墙,要开139端口.这个端口不可以更改

三、如果你不得不用分布式事务,那也得琢磨琢磨 
1.这步操作一定得在事务当中吗?这步操作如果没完成或者失败了,值得回滚整个事务吗?难道没有优雅的补偿措施或者容错措施? 
2.分布式事务涉及到的点,必须的这么多?必须得实时的操作这一大串?不能通过通知类操作去精简掉某些点? 
3.在发起分布式事务之后,你是不是做了事务无关的操作,尽管这些操作跟事务无关?(如,读取数据、计算、等用户返回消息、等其他模块的调用返回等等)要知道事务应该尽快结束。 
4.你没有把一些读操作也算在事务里面了吧?这是很容易犯的错误,你在事务中Enlist了一个select 操作。 
5.你的操作,某些步骤可以等全部操作完成之后再执行.这类操作具有明显的通知类特点。通知类操作是说,我给你一个通知,并且我保证通知到了你;你必须吃下这个通知,并且保证处理成功,但是你不必我一通知你你就处理。这样的操作很明显可以用另外一个任务去搞。

四、使用分布式事务注意如下几点 
1:确保参与事务的machine开启了分布式事务支持; 
2:如果machine开启了防火墙,需要设置msdtc进程为例外; 
3:参与事务的machine不能跨域(如果跨域,目前微软还没有确切的解决方案); 
4:多数据库时才使用分布式事务,如果是同一个数据库,最好使用SqlTransaction.

五、TransactionScope事务级别 
在TransactionScope中默认的事务级别是Serializable,即在事务过程中,完全性锁表。别的进程不能查询,修改,新增,删除。这样会导致效率大大降低,虽然数据完整性很高。通常我们不需要那么高的数据完整性。所以需要修改默认的事务级别: 
所有的事务级别如下

Chaos 
无法改写隔离级别更高的事务中的挂起的更改。

ReadCommitted 
不可以在事务期间读取可变数据,但是可以修改它。

ReadUncommitted 
可以在事务期间读取和修改可变数据。

RepeatableRead 
可以在事务期间读取可变数据,但是不可以修改。可以在事务期间添加新数据。

Serializable 
可以在事务期间读取可变数据,但是不可以修改,也不可以添加任何新数据。

Snapshot 
可以读取可变数据。在事务修改数据之前,它验证在它最初读取数据之后另一个事务是否更改过这些数据。如果数据已被更新,则会引发错误。这样使事务可获取先前提交的数据值。 
在尝试提升以此隔离级别创建的事务时,将引发一个 InvalidOperationException,并产生错误信息“Transactions with IsolationLevel Snapshot cannot be promoted”(无法提升具有 IsolationLevel 快照的事务)。

Unspecified 
正在使用与指定隔离级别不同的隔离级别,但是无法确定该级别。如果设置了此值,则会引发异常。

六、在C#中使用TransactionScope类(分布式事务),则须注意如下事项 
1、在项目中引用using System.Transactions命名空间(先要在添加net组件的引用); 
2、对MSDTC组件设置: 
在控制面板—>管理工具—>服务 中,开启Distributed Transaction Coordinator 服务。

a、控制面板->管理工具->组件服务->计算机->我的电脑->右键->属性 
b、选择MSDTC页, 确认”使用本地协调器” 
c、点击下方”安全配置”按钮

d、勾选: “允许网络DTC访问”,”允许远程客户端”,”允许入站”,”允许出站”,”不要求进行身份验证”. 
e、对于数据库服务器端, 可选择”要求对呼叫方验证” 
f、勾选:”启用事务Internet协议(TIP)事务”。 
g、在双方防火墙中增加MSDTC.exe例外 
可用命令行: netsh firewall set allowedprogram %windir%/system32/msdtc.exe MSDTC enable 
3、重启IIS服务器。

注意: 
我们只要确保数据库的打开操作是在事务范围内打开就行了。这样就可以做到事务的正确操作。 
如果WEB服务器和数据库是在同一台服务器上,TransactionScope使用的是本地事务,这时不需要配置MSDTC。

如果WEB服务器和数据库不在同一台服务器上,TransactionScope会自动提升事务级别为分布式事务,这时就需要配置MSDTC。

七、TransactionScope实例

C# 代码 复制

    /** <summary>
    /// 发送消息
     /// </summary>
    /// <param name="sendUserId"></param>
    /// <param name="toUser">格式7FFA3AF2-E74B-4174-8403-5010C53E49A7|userName,7FFA3AF2-E74B-4174-8403-5010C53E49A7|userName</param>
    /// <param name="content"></param>
    /// <param name="sendedStatus">表示已送</param>
    /// <returns></returns>
    public static int sendMessage(string sendUserId, string toUser, string content, string sendedStatus)
    {           
        int receiveCount = 0;
        TransactionOptions transactionOption = new TransactionOptions();

        //设置事务隔离级别
        transactionOption.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;

        // 设置事务超时时间为60秒
        transactionOption.Timeout = new TimeSpan(0, 0, 60);

        using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, transactionOption))
        {
            try
            {
                //在这里实现事务性工作
 //发送消息
                insertMessage(sendUserId, toUser, content, sendedStatus);

 //在接收信息表中插入记录
                receiveCount += insertReceiveMessage(userids[0], sendUserId, content, "0");

                // 没有错误,提交事务
                scope.Complete();
            }
            catch (Exception ex) {
                throw new Exception("发送信息异常,原因:"+ex.Message);
            }finally{
                //释放资源
                scope.Dispose();
              }                               
        }
        return receiveCount;
    }
类似实例:
  //修改订单状态
        protected void UpdateState_Click(object sender, EventArgs e)
        {
             string id = Request.QueryString["id"];
                int x;
                if (int.TryParse(id, out x))
                {
                    DAL.OrderDAO orderdao = new DAL.OrderDAO();
                    Model.Order od = orderdao.GetModel(x);
                    if (od!=null)
                    {

                        switch (od.state)
                        {
                            case 0:
                                if (chkState.Items[1].Selected == true)
                                {
                                    orderdao.UpdateState(od.id, 1);
                                    Utility.Tool.alert("状态更新成功",Request.Url.ToString(),this.Page);
                                }
                                else
                                {
                                    Utility.Tool.alert("请勾选用户已经划出款", this.Page);
                                    return;
                                }
                                    break;
                            case 3:
                                    if (chkState.Items[4].Selected == true)
                                    {
                                        //这里完成订单,给用户增加积分,我们用事务来处理
                                        using (TransactionScope scope=new TransactionScope())
                                        {
                                            try
                                            {
                                                orderdao.UpdateState(od.id, 4);
                                                //把订单中的商品金额,增加到用户的积分。
                                                int jifen = Convert.ToInt16(od.detailsmoney);
                                                new DAL.UserDAO().UpdateIntegral(User.Identity.Name, jifen);
                                                Utility.Tool.alert("状态更新成功,积分 " + jifen + " 分已经发送", 
Request.Url.ToString(), this.Page);
                                                //处理完事务之后,记得提交,不然,他还是不执行的
                                                scope.Complete();
                                            }
                                            catch (Exception re)
                                            {

                                                Response.Write("事务错误,请联系管理员"+re.Message);
                                                Response.End();

                                            }
                                            
                                        }
                                        
                                    }
                                    else
                                    {
                                        Utility.Tool.alert("请勾选用户已经收到货", this.Page);
                                        return;
                                    }
                                    break;
                            default:
                                break;
                        }
                    }

                }
        }

与其它类型事务比较

1、SQL事务

优点:执行效率最佳

限制:事务上下文仅在数据库中调用,难以实现复杂的业务逻辑。

[sql]  view plain copy 在CODE上查看代码片 派生到我的代码片 在CODE上查看代码片 派生到我的代码片
  1. CREATE PROCEDURE Tran1    
  2. as    
  3. begin tran    
  4. set xact_abort on   
  5. Insert Into trantest (id,test)values(1,'test')    
  6. Insert Into trantest (id,test)values(2,'test')    
  7. commit tran    
  8. GO    
  9. --set xact_abort on  表示遇到错误立即回滚   
  10. --当然你也可以这么写   
  11. CREATE PROCEDURE tran1   
  12. as   
  13. begin tran   
  14. insert into trantest(id,test)values(1,'test')   
  15. if(@@error<>0)   
  16.  rollback tran   
  17. else   
  18.  begin   
  19.   insert into trantest(id,test)values(2,'test')   
  20.   if(@@error<>0)   
  21.    rollback tran   
  22.   else   
  23.    commit tran   
  24.  end   
  25. GO   
[sql]  view plain  copy
  1. CREATE PROCEDURE Tran1    
  2. as    
  3. begin tran    
  4. set xact_abort on   
  5. Insert Into trantest (id,test)values(1,'test')    
  6. Insert Into trantest (id,test)values(2,'test')    
  7. commit tran    
  8. GO    
  9. --set xact_abort on  表示遇到错误立即回滚   
  10. --当然你也可以这么写   
  11. CREATE PROCEDURE tran1   
  12. as   
  13. begin tran   
  14. insert into trantest(id,test)values(1,'test')   
  15. if(@@error<>0)   
  16.  rollback tran   
  17. else   
  18.  begin   
  19.   insert into trantest(id,test)values(2,'test')   
  20.   if(@@error<>0)   
  21.    rollback tran   
  22.   else   
  23.    commit tran   
  24.  end   
  25. GO   

2、ADO.NET 事务 在ADO.NET 中,可以使用Connection 和Transaction 对象来控制事务。若要执行事务,请执行下列操作: 调用Connection 对象的BeginTransaction 方法来标记事务的开始。 将Transaction 对象分配给要执行的Command的Transaction 属性。 执行所需的命令。

调用Transaction 对象的Commit 方法来完成事务,或调用Rollback 方法来取消事务。

优点:简单,效率和数据库事务差不多快。

缺点:事务执行在数据库连接层上,所以你需要在事务过程中手动的维护一个连接。

[csharp]  view plain copy 在CODE上查看代码片 派生到我的代码片 在CODE上查看代码片 派生到我的代码片
  1. SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Test"].ConnectionString);  
  2.  conn.Open();  
  3.  SqlTransaction tx = conn.BeginTransaction(IsolationLevel.ReadCommitted);  
  4.  SqlCommand cmd = new SqlCommand();  
  5.  cmd.Connection = conn;  
  6.  cmd.Transaction = tx;  
  7.    
  8.  try  
  9.  {  
  10.      cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试1','1')";  
  11.      cmd.ExecuteNonQuery();  
  12.      cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试2','2')";  
  13.      cmd.ExecuteNonQuery();  
  14.    
  15.      tx.Commit();  
  16.  }  
  17.  catch (Exception ex)  
  18.  {  
  19.      tx.Rollback();  
  20.      throw new Exception(ex.Message, ex);  
  21.  }  
  22.  finally  
  23.  {  
  24.      conn.Close();  
  25.  }  
[csharp]  view plain  copy
  1. SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Test"].ConnectionString);  
  2.  conn.Open();  
  3.  SqlTransaction tx = conn.BeginTransaction(IsolationLevel.ReadCommitted);  
  4.  SqlCommand cmd = new SqlCommand();  
  5.  cmd.Connection = conn;  
  6.  cmd.Transaction = tx;  
  7.    
  8.  try  
  9.  {  
  10.      cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试1','1')";  
  11.      cmd.ExecuteNonQuery();  
  12.      cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试2','2')";  
  13.      cmd.ExecuteNonQuery();  
  14.    
  15.      tx.Commit();  
  16.  }  
  17.  catch (Exception ex)  
  18.  {  
  19.      tx.Rollback();  
  20.      throw new Exception(ex.Message, ex);  
  21.  }  
  22.  finally  
  23.  {  
  24.      conn.Close();  
  25.  }  

3、TransactionScope事务 在.NET 2.0中新添加了一个名为System.Transactions的命名空间,其提供了一个“轻量级”的、易于使用的事务框架,通过这个框架可以大大简化事务的操作。 这个框架提供了如下优点: (1)在简单(不涉及分布式)事务中也可以使用声明式的事务处理方法,而不必使用Com+容器和目录注册。 (2)用户根本不需要考虑是简单事务还是分布式事务。它实现一种所谓自动提升事务机制(Promotable Transaction),会自动根据事务中涉及的对象资源判断使用何种事务管理器。 TransactionScope事务类,它可以使代码块成为事务性代码。并自动提升为分布式事务 优点:实现简单,同时能够自动提升为分布式事务

[csharp]  view plain copy 在CODE上查看代码片 派生到我的代码片 在CODE上查看代码片 派生到我的代码片
  1. TransactionOptions option = new TransactionOptions();  
  2. option.IsolationLevel = IsolationLevel.ReadCommitted;  
  3.   
  4. using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required,option))  
  5. {  
  6.     using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Test"].ConnectionString))  
  7.     {  
  8.         conn.Open();  
  9.         SqlCommand cmd = new SqlCommand(conn);  
  10.         cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试1','1')";  
  11.         cmd.ExecuteNonQuery();  
  12.         cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试2','2')";  
  13.         cmd.ExecuteNonQuery();  
  14.     }  
  15.     ts.Complete();  
  16. }  
[csharp]  view plain  copy
  1. TransactionOptions option = new TransactionOptions();  
  2. option.IsolationLevel = IsolationLevel.ReadCommitted;  
  3.   
  4. using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required,option))  
  5. {  
  6.     using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Test"].ConnectionString))  
  7.     {  
  8.         conn.Open();  
  9.         SqlCommand cmd = new SqlCommand(conn);  
  10.         cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试1','1')";  
  11.         cmd.ExecuteNonQuery();  
  12.         cmd.CommandText = "INSERT INTO [Test]([Name],[Value]) VALUES ('测试2','2')";  
  13.         cmd.ExecuteNonQuery();  
  14.     }  
  15.     ts.Complete();  
  16. }  

4、EnterpriseServices实现事务

就是利用com+实现自动处理事务。

添加引用System.EnterpriseServices.dll using System.EnterpriseServices; 

使用方法参考:http://support.microsoft.com/default.aspx?scid=kb;zh-cn;816141 随便建立一个按钮,在按钮中进行如下操作:

[csharp]  view plain copy 在CODE上查看代码片 派生到我的代码片 在CODE上查看代码片 派生到我的代码片
  1. try    
  2. {    
  3.  work1();    
  4.  work2();    
  5.  ContextUtil.SetComplete();    
  6. }    
  7. catch(System.Exception except)    
  8. {    
  9.  ContextUtil.SetAbort();    
  10.  Response.Write(except.Message);    
  11. }    
[csharp]  view plain  copy
  1. try    
  2. {    
  3.  work1();    
  4.  work2();    
  5.  ContextUtil.SetComplete();    
  6. }    
  7. catch(System.Exception except)    
  8. {    
  9.  ContextUtil.SetAbort();    
  10.  Response.Write(except.Message);    
  11. }    

然后在页面中添加2个操作,模拟一下在逻辑层调用不同类中的操作的情况 :

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private void work1()    
  2.   {    
  3.    SqlConnection conn=new SqlConnection(System.Configuration.ConfigurationSettings.AppSettings["conn"]);    
  4.    SqlCommand cmd1=new SqlCommand("Insert Into trantest (id,test)values(1,'test')",conn);    
  5.    conn.Open();    
  6.    cmd1.ExecuteNonQuery();    
  7.    conn.Close();    
  8.   }    
  9.   
  10.   private void work2()    
  11.   {    
  12.    SqlConnection conn=new SqlConnection(System.Configuration.ConfigurationSettings.AppSettings["conn"]);    
  13.    SqlCommand cmd2=new SqlCommand("Insert Into trantest (id,test)values(2,'test')",conn);    
  14.    conn.Open();    
  15.    cmd2.ExecuteNonQuery();    
  16.    conn.Close();    
  17.   }    
  18.   
  19. 修改前台页面在<%Page后面添加 Transaction="Required" 即可  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在高并发下插入数据前进行验证是否存在,可以采用数据库事务与唯一性约束(Unique Constraint)相结合的方案。 具体实现步骤如下: 1. 在数据库中创建唯一性约束,这样可以保证数据库中某个字段的值不重复。 2. 在 C#使用事务进行操作,事务可以保证多个操作要么全部成功,要么全部失败。 3. 在事务中进行数据查询操作,如果查询结果为空,则说明该数据不存在,可以进行插入操作;如果查询结果不为空,则说明该数据已经存在,不进行插入操作。 以下是一个示例代码: ```csharp using System.Data.SqlClient; using System.Transactions; public class DataAccess { private const string ConnectionString = "your_connection_string_here"; public bool InsertData(string data) { bool result = false; using (TransactionScope scope = new TransactionScope()) { try { using (SqlConnection connection = new SqlConnection(ConnectionString)) { connection.Open(); // 查询数据是否存在 string query = "SELECT COUNT(*) FROM YourTable WHERE Data = @Data"; SqlCommand command = new SqlCommand(query, connection); command.Parameters.AddWithValue("@Data", data); int count = (int)command.ExecuteScalar(); if (count == 0) { // 数据不存在,进行插入操作 string insert = "INSERT INTO YourTable(Data) VALUES(@Data)"; SqlCommand insertCommand = new SqlCommand(insert, connection); insertCommand.Parameters.AddWithValue("@Data", data); insertCommand.ExecuteNonQuery(); result = true; } else { // 数据已经存在,不进行插入操作 result = false; } } scope.Complete(); } catch (Exception ex) { // 处理异常 result = false; } } return result; } } ``` 以上代码中使用事务TransactionScope)来保证操作的原子性,使用了数据库查询语句(SELECT)来查询数据是否存在,使用了数据库插入语句(INSERT)来插入数据。同时,使用了参数化查询(Parameters.AddWithValue)来防止 SQL 注入攻击。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值