下面是我写的一个数据库操作类,每一个函数执行不同的数据库操作:
using System.Data.SqlClient;namespace CWMS.DataStore
{
    public partial class Bid
    {        public void do1()
        {
            SQL操作1
        }
        public void do2()
        {
    SQL操作2
        }        public void do3()
        {
            SQL操作3
        }
        public void do4()
        {
           SQL操作4
        }
        public void do5()
        {
SQL操作5
        }
    }
}
下面是我在程序中调用这类进行相关数据库操作:
 private CWMS.DataStore.Bid bid = new CWMS.DataStore.Bid();
 public override void Submit()
{
……
 bid.do5();
 if (成功)
  {

bid.do1();

bid.do2();

bid.do3();

bid.do4();
  }
}
问题:
我想实现,对任一个函数如果没有执行成功,则rollback;
如果是在一个函数中用事务处理我会用,但像这种多个函数的事务处理操作应该怎么写呢?听说可以用函数传递事务参数的方法,但是不会用,请大家帮帮忙。

解决方案 »

  1.   

    没有用过com+,能说说怎么用吗?
      

  2.   

    ms-help://MS.MSDNQTR.2003FEB.2052/cpguide/html/cpconautomatictransactionprocessing.htm
      

  3.   

    调用一个函数不成功的情况下一般都是系统抛出或者自己抛出的异常,在try{}catch(){}中rollback就行了,不过sql操作对象,比如SqlCommand要变成类成员
      

  4.   

    public static string strDatabaseLogicName = ConfigurationSettings.AppSettings["CommDataAccess"];
            /// <summary>
            /// delete the data from cstiim,cstmbm,cstpim,cstpnm,cstpcm,cstlrm,csterm,cstpfm
            /// when delete Cost Set From csm
            /// </summary>
            /// <param name="strCost"></param>
            /// <param name="strError"></param>
            /// <returns></returns>
            public static int DeleteDataCost(string strCost,out string strError)
            {
                int nRet = 0;           DbAccess dbAccess = new DbAccess(strDatabaseLogicName);
               dbAccess.Conn.Open();
               IDbTransaction Transaction = dbAccess.Conn.BeginTransaction();            CstiimService service=new CstiimService();
                try
                {
                    CstiimSQL.DeleteDataCost(service, Transaction, strCost);                CstmbmSQL.DeleteDataCost(service, Transaction, strCost);                CstpimSQL.DeleteDataCost(service, Transaction, strCost);                CstpnmSQL.DeleteDataCost(service, Transaction, strCost);                CstpcmSQL.DeleteDataCost(service, Transaction, strCost);                CstlrmSQL.DeleteDataCost(service, Transaction, strCost);                CstermSQL.DeleteDataCost(service, Transaction, strCost);                CstpfmSQL.DeleteDataCost(service, Transaction, strCost);                CsmSQL.DeleteDataCost(service, Transaction, strCost);
    }其中
      public static void DeleteDataCost(Service service, IDbTransaction transaction, string strCost)
            {
                string strCommand = "delete  from cstiim where cfcset='" + strCost + "'";            DbAccess dbAccess = new DbAccess(service.Database);            try
                {
                    dbAccess.ExecuteNonQuery(transaction, CommandType.Text, strCommand);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    其余的都一样
      

  5.   

    COM+赞同!在方法前加个属性就可以了[AutoComplete]
      

  6.   

    谢谢了,不过还得请教请教:
    CstiimService service=new CstiimService();
    CstiimService是你自己定义的类吗?
    CstiimSQL.DeleteDataCost(service, Transaction, strCost);
    这里的service, Transaction都是做为参数传过来的吗?传递service这个参数是什么意思?还有这个事务处理在什么地方结束呢?
      

  7.   

    只有在每个方法中用个事物对象就行
    public void DataOperate1(DbTransaction tran,  ...){...} //在函数体内不要提交事务
    public void DataOperate2(DbTransaction tran,  ...){...} //在函数体内不要提交事务public void Do()
    {
       DbConnection conn= new SqlConnection("...");
       try{
       conn.Open();
       DbTransaction tran = conn.BeginTransaction();
       DataOperate1(tran,...);
      DataOperate2(tran, ...);
       tran.Commit();
      }
      catch
    {
       if(tran != null){tran.RollBack();}
    }
    }
      

  8.   

    一种法子是使用com+事务,不过比较耗资源还有一个法子是使用同一个数据库连接,用数据库事务来控制
      

  9.   

    public void DataOperate1(DbTransaction tran,  ...){...} //在函数体内不要提交事务
    在这个函数体内应该怎样处理事务呢?还是在函数体内不做任何处理,只需要将事务做为参数传过来就可以了吗?
    还有,必须用同一个数据库连接吗?
    我在写程序的时候,用了类封装,将所有对数据库的操作封装在一个类里;在程序代码里调用这个类执行对数据库的操作;这样就导致我很难用一个数据库连接来做所有的数据库操作。
      

  10.   

    两种:
    1.Connection.BeginTrans()
    2.using System.Transactions;
      

  11.   

    根本不需要用什么com+啊,这种是最基础的事务应用。要确保所有函数用同一个连接,传递同一个事务,在最初时打开该连接,开启一个事务,然后将该事务作为参数传递给每个函数,当所有函数都执行完毕时调用Commit,否则Rollback。
    例如
    DbConnection conn= new SqlConnection("...");
       try{
       conn.Open();
       DbTransaction tran = conn.BeginTransaction();
    bool success=false;
    if(bid.do1(tran))
     if(bid.do2(tran))
      if(bid.do3(tran))
        if(bid.do4(tran))
          if(bid.do5(tran))
            success=true;
    if(success)
       tran.Commit();
    else
       tran.RollBack();
    }
    catch
    {
    tran.RollBack();
    }
    finally
    {
    最好检测一下连接是否关闭
    }
    至于事务传递的具体实现,可以参考一下著名的SqlHelper类中的底层实现。
      

  12.   

    TransactionScope 类提供一个简单方法,通过这一方法,您不必与事务本身交互,即可将代码块标记为参与某个事务。事务范围可以自动选择和管理环境事务。由于它易于使用并且效率很高,因此建议您在开发事务应用程序时使用 TransactionScope 类。 此外,您不必显式向事务登记资源。任何 System.Transactions 资源管理器(例如 SQL Server 2005)都可以检测到该范围创建的环境事务的存在并自动登记。创建事务范围
    下面的示例说明 TransactionScope 类的简单用法。Visual Basic  复制代码 
    Using scope As TransactionScope = New TransactionScope()    'Create and open the SQL connection.  The work done on this connection will be a part of the transaction created by the TransactionScope
        Dim myConnection As New SqlConnection("server=(local)\SQLExpress;Integrated Security=SSPI;database=northwind")
        Dim myCommand As New SqlCommand()
        myConnection.Open()
        myCommand.Connection = myConnection    'Restore database to near it's original condition so sample will work correctly.
        myCommand.CommandText = "DELETE FROM Region WHERE (RegionID = 100) OR (RegionID = 101)"
        myCommand.ExecuteNonQuery()    'Insert the first record.
        myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (100, 'MidWestern')"
        myCommand.ExecuteNonQuery()    'Insert the second record.
        myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (101, 'MidEastern')"
        myCommand.ExecuteNonQuery()    myConnection.Close()    'Call complete on the TransactionScope or not based on input
        Dim c As ConsoleKeyInfo
        While (True)        Console.Write("Complete the transaction scope? [Y|N] ")
            c = Console.ReadKey()
            Console.WriteLine()
            If (c.KeyChar = "Y") Or (c.KeyChar = "y") Then
                scope.Complete()
                Exit While
            ElseIf ((c.KeyChar = "N") Or (c.KeyChar = "n")) Then
                Exit While
            End If
        End WhileEnd Using 
    C#  复制代码 
    using (TransactionScope ts = new TransactionScope())
    {
        //Create and open the SQL connection.  The work done on this connection will be a part of the transaction created by the TransactionScope
        SqlConnection myConnection = new SqlConnection("server=(local)\\SQLExpress;Integrated Security=SSPI;database=northwind");
        SqlCommand myCommand = new SqlCommand();
        myConnection.Open();
        myCommand.Connection = myConnection;    //Restore database to near it's original condition so sample will work correctly.
        myCommand.CommandText = "DELETE FROM Region WHERE (RegionID = 100) OR (RegionID = 101)";
        myCommand.ExecuteNonQuery();    //Insert the first record.
        myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (100, 'MidWestern')";
        myCommand.ExecuteNonQuery();    //Insert the second record.
        myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (101, 'MidEastern')";
        myCommand.ExecuteNonQuery();    myConnection.Close();                    //Call complete on the TransactionScope or not based on input
        ConsoleKeyInfo c;
        while (true)
        {
                                Console.Write("Complete the transaction scope? [Y|N] ");
            c = Console.ReadKey();
            Console.WriteLine();        if ((c.KeyChar == 'Y') || (c.KeyChar == 'y'))
            {
                // Commit the transaction
                ts.Complete();
                break;
            }
            else if ((c.KeyChar == 'N') || (c.KeyChar == 'n'))
            {
                break;
            }
        } 您创建了新的 TransactionScope 对象后,即开始事务范围。 如代码示例中所示,建议您使用 using 语句创建范围。C# 和 Visual Basic 中都提供 using 语句,其工作方式类似于 try...finally 块,可确保正确处理范围。 实例化 TransactionScope 时,事务管理器会确定要参与的事务。确定之后,该范围将始终参与此事务。这个决定基于两个因素:环境事务是否存在以及构造函数中 TransactionScopeOption 参数的值。环境事务是在其中执行代码的事务。您可以通过调用 Transaction 类的静态 Current 属性,获取对环境事务的引用。有关如何使用此参数的更多信息,请参见本主题的使用 TransactionScopeOption 管理事务流一节。完成事务范围
    在您的应用程序完成了它要在某一事务中执行的所有工作后,应该只调用 Complete 方法一次,以便通知事务管理器它可以提交该事务。强烈建议将对 Complete 的调用作为最后一条语句放置在 using 块中。 如果调用此方法失败,则会中止该事务,因为事务管理器会将此解释为一个系统故障,或者与在事务范围内引发的异常等效的故障。但是,调用此方法并不确保将提交该事务。这只是一个向事务管理器通知您的状态的方法。在调用 Complete 方法后,您将无法再通过 Current 属性访问环境事务,这样做将会引发异常。 如果 TransactionScope 对象最初创建了该事务,则通过事务管理器提交该事务的实际工作将在 using 块中的最后一行代码后发生。如果它没有创建该事务,则只要 CommittableTransaction 对象的所有者调用了 Commit,就发生提交。在该时刻,事务管理器将基于对 TransactionScope 对象是否已调用了 Complete 方法,调用资源管理器并通知它们提交或回滚。using 语句确保调用 TransactionScope 对象的 Dispose 方法,即使发生异常也是如此。Dispose 方法标记事务范围的结束。在调用此方法后发生的异常不会影响该事务。该方法还将环境事务还原为其以前的状态。 如果范围创建该事务,则引发 TransactionAbortedException,并中止该事务。如果事务管理器无法达成提交决定,则引发 TransactionIndoubtException。如果提交该事务,则不会引发任何异常。回滚事务
    要回滚某个事务,不应在该事务范围内调用 Complete 方法。例如,您可以在该范围内引发异常。该异常所参与的事务将被回滚。
      

  13.   

    先谢谢楼上的这位朋友,不过这些帮助信息我在VS.Net中看过好几遍了,但是没有找到很好的办法。我正在按照大家的方法,试着做。可是我不明白,为什么一定要用同一个数据库连接呢?
      

  14.   

    在使用TransactionScope的时候,
    TransactionScope事务范围内也可以使用不同的数据库联接的。
      

  15.   

    传统的方法一定要用一个数据库连接的,不然就属于分布式事务了,不好控制。TransactionScope虽然可以实现跨数据库的事务,但最好慎用,性能不是很高,另外他在分布式事务中是依赖DTS的,要手动配置。如果你只是在一个数据库上使用事务,那就用一个连接好了,何必多事呢?
      

  16.   

    我为这个问题发的一个帖子,曾经列为CSDN头条http://community.csdn.net/Expert/TopicView.asp?id=5147435如果你还不能解决问题,大家可以一起研究
      

  17.   

    下面是我写的一个数据库操作类,每一个函数执行不同的数据库操作,而且每一个函数里都会New 一个数据库连接,在函数后面再用close()关闭:
    using System.Data.SqlClient;namespace CWMS.DataStore
    {
        public partial class Bid
        {        public void do1()
            {
          System.Data.SqlClient.SqlConnection myConn = new System.Data.SqlClient.SqlConnection(Form1.myConnString);
                System.Data.SqlClient.SqlCommand myCmd = new System.Data.SqlClient.SqlCommand();
                System.Data.SqlClient.SqlDataAdapter myDA = new System.Data.SqlClient.SqlDataAdapter();
                System.Data.DataSet myDS = new System.Data.DataSet();
                DataTable myTable = new DataTable();
                try
                {
                    myConn.Open();
                    SQL操作1
                }
        catch
        {
         }
        finaly
        {
           mycon.close();
        }
            } 
    do2(),do3(),do4(),do5(),这几个函数都是一样处理的。
        }
    }
    下面我进行了一下修改:
    namespace CWMS.DataStore
    {
        public partial class Bid
        {
    System.Data.SqlClient.SqlConnection myConn = new System.Data.SqlClient.SqlConnection(Form1.myConnString);
    System.Data.SqlClient.SqlCommand myCmd = new System.Data.SqlClient.SqlCommand();
            System.Data.SqlClient.SqlDataAdapter myDA = new System.Data.SqlClient.SqlDataAdapter();
            System.Data.DataSet myDS = new System.Data.DataSet();
            DataTable myTable = new DataTable();
      SqlTransaction mytran;
    mytran = myConn.BeginTransaction();        public void do1(mytran)
            {
                myCmd.Transaction = mytran;
                myCmd.Connection = myConn;
              SQL操作1 
            } 
    public void do2(mytran)
            {
                myCmd.Transaction = mytran;
                myCmd.Connection = myConn;
              SQL操作2 
            } 
    ……
    public void dolast()
    {
    if(true)
                       {
                         mytran.commit();
                       }
                       else
                       {
                         mytran.rollback();
                       }
    }
        }
    }
    下面是我在程序中调用这类进行相关数据库操作:
     private CWMS.DataStore.Bid bid = new CWMS.DataStore.Bid();
    …… public override void Submit()
    {
    ……
     bid.do5();
     if (成功)
      {

    bid.do1();

    bid.do2();

    bid.do3();

    bid.do4();

    bid.dolast();
      }
    }
    那如果我这样改一下,是不是可行呢?
      

  18.   

    using(SqlConnection conn = new SqlConnection(connStr))
    {
       conn.Open();
       SqlCommand cmd = new SqlCommand();
       SqlTransaction tr = conn.BeginTransaction();
       //应用程序通过在 SqlConnection 对象上调用 BeginTransaction 来创建 SqlTransaction 对象。
      //SqlTransaction表示要在 SQL Server 数据库中处理的 Transact-SQL 事务。
       cmd.Transaction = tr;
       cmd.Connection = conn;   try
         {
            cmd.CommandText = "insert into employees(employeeid, lastname, firstname)    values(14, 'aaa', 'bbb')";
            cmd.ExecuteNonQuery();        cmd.CommandText = "insert into employees(lastname, firstname) values    ('eeeeee', 'bbb')";
            cmd.ExecuteNonQuery();        tr.Commit();
            }
         catch
           {
           tr.Rollback();
           }}
      

  19.   

    你可以把数据库的操作分别写成函数
    然后再用Transaction调用
      

  20.   

    是啊,我现在就是把数据库的操作分别写成函数;但现在的问题就是如何用Transaction调用的问题。