.net 事务处理

方法1:直接写入到sql 中
在存储过程中使用 BEGIN TRANS, COMMIT TRANS, ROLLBACK TRANS 实现
begin trans
declare @orderDetailsError int,@procuntError int
delete from [order details] where productid=42
select @orderDetailsError =@@error
delete from products where productid=42
select @procuntError=@@error
if(@orderDetailsError =0 and @procuntError=0)
COMMIT TRANS
else
ROLLBACK TRANS
优点:
    所有事务逻辑包含在一个单独的调用中
    拥有运行一个事务的最佳性能
    独立于应用程序
限制:
    事务上下文仅存在于数据库调用中
    数据库代码与数据库系统有关
方法2 :使用ADO.Net 实现
使用ADO.Net 实现,使用这种方式的优点是可以在中间层来管理事务,当然你也可以选择在数据层来实现。
SqlConnection 和OleDbConnection    对象有一个 BeginTransaction 方法,它可以返回 SqlTransaction
或者OleDbTransaction 对象。而且这个对象有 Commit 和 Rollback 方法来管理事务
     SqlConnection sqlConnection = new SqlConnection("workstation id=WEIXIAOPING;packet size=4096;user id=sa;initial catalog=Northwind;persist security info=False");
     sqlConnection.Open();
     SqlTransaction    myTrans = sqlConnection.BeginTransaction();
     SqlCommand sqlInsertCommand = new SqlCommand();
     sqlInsertCommand.Connection = sqlConnection
     sqlInsertCommand.Transaction=myTrans;
     try{
         sqlInsertCommand.CommandText="insert into tbTree(Context,ParentID) values(‘北京‘,1)";
         sqlInsertCommand.ExecuteNonQuery();
         sqlInsertCommand.CommandText="insert into tbTree(Context,ParentID) values(‘上海‘,1)";
         sqlInsertCommand.ExecuteNonQuery();
         myTrans.Commit();
       }catch(Exception ex)
       {
        myTrans.Rollback();
       }
      finally
      {
       sqlConnection.Close();
      }
优点:
       简单性
       和数据据事务差不多的快
       独立于数据库,不同数据库的专有代码被隐藏了
缺点:
       事务不能跨越多个数据库连接
       事务执行在数据库连接层上,所以需要在事务过程中维护一个数据库连接
       ADO.Net分布事务也可以跨越多个数据库,但是其中一个SQL SERVER 数据库的话,通过用SQL SERVER连接服务器连接到别的数据库,但是如果是在DB2和Orcal之间就不可以。
以上两种事务是经常用到的事务处理方法。

 

 

ASP.NET事务可以说是在.NET平台上事务实现方式最简单的一种,你仅仅需要一行代码即可。在aspx的页面声明中加一个额外的属性,即事务属性Transaction="Required",它有如下的值:Disabled(默认)、NotSupported、Supported、Required和RequiresNew,这些设置和COM+及企业级服务中的设置一样,典型的一个例子是如果你想在页面上下文中运行事务,那么要将其设置为Required。如果页面中包含有用户控件,那么这些控件也会包含到事务中,事务会存在于页面的每个地方。

         代码示例:


页面声明Transaction="Required":

<%@ Page Transaction="Required"  Language="C#" AutoEventWireup="true"

CodeBehind="WebForm3.aspx.cs" Inherits="WebApplication4.WebForm3" %>

页面引用:using System.EnterpriseServices;。

然后,数据操作代码:

protected void Button1_Click(object sender, EventArgs e)
{
    try
    {
        Work1();
        Work2();
        ContextUtil.SetComplete();   //提交事务
    }
    catch (System.Exception except)
    {
        ContextUtil.SetAbort();      //撤销事务
        Response.Write(except.Message);
    }  

private void Work1()
{
    string conString = "data source=127.0.0.1;database=codematic;user id=sa;password=";
    SqlConnection myConnection = new SqlConnection(conString);
    string strSql = "Insert Into P_Category(CategoryId,Name)values(‘1‘,‘test1‘)";
    SqlCommand myCommand = new SqlCommand(strSql, myConnection);
    myConnection.Open();
    int rows = myCommand.ExecuteNonQuery();
    myConnection.Close();

}

private void Work2()
{
    string conString = "data source=127.0.0.1;database=codematic;user id=sa;password=";
    SqlConnection myConnection = new SqlConnection(conString);
    string strSql = "Insert Into P_Category(CategoryId,Name)values(‘2‘,‘test2‘)";
    SqlCommand myCommand = new SqlCommand(strSql, myConnection);
    myConnection.Open();
    int rows = myCommand.ExecuteNonQuery();
    myConnection.Close();
}

 

 .NET Framework 2.0中增加了System.Transactions,这是一种新的命名空间,完全专注于控制事务性行为。引入了执行事务性工作的更简单方法及一些新的性能优化。System.Transactions提供了一个“轻量级”的、易于使用的Transaction框架。

在上节中,要实现Transaction需要利用EnterpriseServices,让组件从ServiceComponent继承下来。而通过System.Transactions,则只要简单的几行代码,不需要继承,不需要Attribute标记。用户根本不需要考虑是简单事务还是分布式事务。新模型会自动根据事务中涉及的对象资源判断使用何种事务管理器。简而言之,对于任何的事务,用户只要使用同一种方法进行处理即可。

下面介绍System.Transactions的几种用法。

首先要引用:using System.Transactions;

其次,将事务操作代码放在TransactionScope中执行。如:

using (TransactionScope ts = new TransactionScope())

{

    //事务操作代码

    ts.Complete();

}

代码示例:

这是最简单,也是最常见的用法。创建了新的 TransactionScope 对象后,即开始创建事务范围。如代码示例所示,建议使用 using 语句创建范围。位于 using 块内的所有操作将成为一个事务的一部分,因为它们共享其所定义的事务执行上下文。本例中的最后一行,调用 TransactionScope  Complete 方法,将导致退出该块时请求提交该事务。此方法还提供了内置的错误处理,出现异常时会终止事务。

using (TransactionScope ts = new TransactionScope())//使整个代码块成为事务性代码

{

    #region 在这里编写需要具备Transaction的代码

    string msg = "";

    string conString = "data source=127.0.0.1;database=codematic;user id=sa;

password=";

    SqlConnection myConnection = new SqlConnection(conString);

    myConnection.Open();

    SqlCommand myCommand = new SqlCommand();

    myCommand.Connection = myConnection;

    try

    {

        myCommand.CommandText = "update P_Product set Name=‘电脑2‘ where Id=52";

        myCommand.ExecuteNonQuery();

        myCommand.CommandText = "update P_Product set Name=‘电脑3‘ where Id=53";

        myCommand.ExecuteNonQuery();

        msg = "成功!";

    }

    catch (Exception ex)

    {

        msg = "失败:" + ex.Message;

    }

    finally

    {

        myConnection.Close();

    }

    #endregion

    ts.Complete();

    return msg;              

}           

上面的代码演示了在一个Transaction Scope里面打开一个数据库连接的过程。这个数据库连接由于处在一个Transaction Scope里面,所以会自动获得Transaction的能力。如果这里数据库连接的是SQL Server 2005,那么这个Transaction将不会激活一个MSDTC管理的分布式事务,而是会由.NET创建一个Local Transaction,性能非常高。但是如果是SQL Server 2000,则会自动激活一个分布式事务,在性能上会受一定的损失。

再看下面的例子:

void MethodMoreConn()

{

    using (TransactionScope ts = new TransactionScope())

    {

        using (SqlConnection conn = new SqlConnection(conString1))

        {

            conn.Open();

            using (SqlConnection conn2 = new SqlConnection(conString2))

            {

                conn2.Open();

            }

        }

        ts.Complete();

    }

}

这个例子更加充分地说明了Transaction Scope的强大,两个数据库连接!虽然上面的connconn2是两个不同的连接对象,可能分别连接到不同的数据库,但是由于它们处在一个TransactionScope中,它们就具备了“联动”的Transaction能力。在这里,将自动激活一个MSDTC管理的分布式事务(可以通过打开【管理工具】里面的组件服务,来查看当前的分布式事务列表)。

1.在分布式事务中登记

ADO.NET 2.0 中的新增功能支持使用 EnlistTransaction 方法在分布式事务中登记。由于 EnlistTransaction  Transaction 实例中登记连接,因此,该方法利用 System.Transactions 命名空间中的可用功能来管理分布式事务,从而比使用 System.EnterpriseServices. ITransaction 对象的 EnlistDistributedTransaction 更可取。此外,其语义也稍有不同:在一个事务中显式登记了某个连接后,如果第一个事务尚未完成,则无法取消登记或在另一个事务中登记该连接。

void MethodEnlist()

{

    CommittableTransaction tx = new CommittableTransaction();

    using (SqlConnection conn = new SqlConnection(conString))

    {

        conn.EnlistTransaction(tx);

    }

    tx.Commit();

}

2.实现嵌套事务范围

void RootMethod()

{

    using (TransactionScope scope = new TransactionScope())

    {

        //操作代码

        SonMethod();//子事务方法

        scope.Complete();

    }

}

void SonMethod()

{

    using (TransactionScope scope = new TransactionScope())

    {

        //操作代码

        scope.Complete();

    }

}

3.事务范围附加选项

如果你想要保留代码部分执行的操作,并且在操作失败的情况下不希望中止环境事务,则Suppress对你很有帮助。例如,在你想要执行日志记录或审核操作时,不管你的环境事务是提交还是中止,上述值都很有用。该值允许你在事务范围内具有非事务性的代码部分,如以下示例所示。

void MethodSuppress()

{

    using (TransactionScope scope1 = new TransactionScope())//开始事务

    {

        try

        {

           //开始一个非事务范围

            using (TransactionScope scope2 = new TransactionScope(

               TransactionScopeOption.Suppress))

            {

                //不受事务控制代码

            }

            //从这里开始又回归事务处理

        }

        catch

        { }       

   }

}

虽然.NET 2.0对事务提供了很好的支持,但是没有必要总是使用事务。使用事务的第一条规则是,在能够使用事务的时候都应该使用事务,但是不要使用过度。原因在于,每次使用事务都会占用一定的开销。另外,事务可能会锁定一些表的行。还有一条规则是,只有当操作需要的时候才使用事务。例如,如果只是从数据库中查询一些记录,或者执行单个查询,则在大部分时候都不需要使用显式事务。

开发人员应该在头脑中始终保持一个概念,就是用于修改多个不同表数据的冗长事务会严重妨碍系统中的所有其他用户。这很可能导致一些性能问题。当实现一个事务时,遵循下面的实践经验能够达到可接受的结果:

l 避免使用在事务中的Select返回数据,除非语句依赖于返回数据。

l 如果使用Select语句,则只选择需要的行,这样不会锁定过多的资源,而尽可能地提高性能。

l 尽量将事务全部写在T-SQL或者API中。

l 避免事务与多重独立的批处理工作结合,应该将这些批处理放置在单独的事务中。

l 尽可能避免大量更新。

另外,必须注意的一点就是事务的默认行为。在默认情况下,如果没有显式地提交事务,则事务会回滚。虽然默认行为允许事务的回滚,但是显式回滚方法总是一个良好的编程习惯。这不仅仅只是释放锁定数据,也将使得代码更容易读取并且更少错误。

      .NET提供的事务功能很强大,具体的内容远不止本文所讲解的这样简单。本文只是起到一个抛砖引玉的功能。希望读者能够灵活恰当地使用事务功能,而不要过度使用事务,否则可能会对性能起到消极的作用。 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。