146

在 C# .Net 2.0 中进行事务的最佳实践是什么。应该使用哪些类?有什么需要注意的陷阱等等。所有提交和回滚的东西。我刚刚开始一个项目,我可能需要在将数据插入数据库时​​进行一些事务。欢迎任何关于交易的基本内容的回复或链接。

4

5 回答 5

274

有两种主要的交易类型;连接事务和环境事务。连接事务(例如 SqlTransaction)直接绑定到数据库连接(例如 SqlConnection),这意味着您必须不断传递连接 - 在某些情况下可以,但不允许“创建/使用/释放”使用,并且不允许跨数据库工作。一个例子(格式化为空格):

using (IDbTransaction tran = conn.BeginTransaction()) {
    try {
        // your code
        tran.Commit();
    }  catch {
        tran.Rollback();
        throw;
    }
}

不太乱,但仅限于我们的连接“conn”。如果我们想调用不同的方法,我们现在需要传递“conn”。

另一种选择是环境交易;.NET 2.0 中的新增功能,TransactionScope对象 (System.Transactions.dll) 允许使用一系列操作(合适的提供程序将自动加入环境事务)。这使得改造现有(非事务性)代码变得容易,并与多个提供者交谈(尽管如果您与多个提供者交谈,DTC 将参与其中)。

例如:

using(TransactionScope tran = new TransactionScope()) {
    CallAMethodThatDoesSomeWork();
    CallAMethodThatDoesSomeMoreWork();
    tran.Complete();
}

请注意,这两种方法可以处理它们自己的连接(打开/使用/关闭/处置),但它们会默默地成为环境事务的一部分,而无需我们传入任何内容。

如果您的代码错误,Dispose() 将在没有 Complete() 的情况下被调用,因此它将被回滚。支持预期的嵌套等,尽管您不能回滚内部事务但完成外部事务:如果有人不满意,事务将中止。

TransactionScope 的另一个优点是它不仅与数据库相关联;它还与数据库相关联。任何事务感知提供商都可以使用它。例如,WCF。或者甚至还有一些与 TransactionScope 兼容的对象模型(即具有回滚功能的 .NET 类 - 可能比纪念品更容易,尽管我自己从未使用过这种方法)。

总而言之,一个非常非常有用的对象。

一些警告:

  • 在 SQL Server 2000 上,TransactionScope 将立即转到 DTC;这在 SQL Server 2005 及更高版本中已修复,它可以使用 LTM(开销要少得多),直到您与 2 个源等交谈,当它被提升到 DTC 时。
  • 有一个小故障意味着您可能需要调整连接字符串
于 2008-10-22T06:48:26.640 回答
11
protected void Button1_Click(object sender, EventArgs e)
   {


       using (SqlConnection connection1 = new SqlConnection("Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\Database.mdf;Integrated Security=True;User Instance=True"))
       {
           connection1.Open();

           // Start a local transaction.
           SqlTransaction sqlTran = connection1.BeginTransaction();

           // Enlist a command in the current transaction.
           SqlCommand command = connection1.CreateCommand();
           command.Transaction = sqlTran;

           try
           {
               // Execute two separate commands.
               command.CommandText =
                "insert into [doctor](drname,drspecialization,drday) values ('a','b','c')";
               command.ExecuteNonQuery();
               command.CommandText =
                "insert into [doctor](drname,drspecialization,drday) values ('x','y','z')";
               command.ExecuteNonQuery();

               // Commit the transaction.
               sqlTran.Commit();
               Label3.Text = "Both records were written to database.";
           }
           catch (Exception ex)
           {
               // Handle the exception if the transaction fails to commit.
               Label4.Text = ex.Message;


               try
               {
                   // Attempt to roll back the transaction.
                   sqlTran.Rollback();
               }
               catch (Exception exRollback)
               {
                   // Throws an InvalidOperationException if the connection 
                   // is closed or the transaction has already been rolled 
                   // back on the server.
                   Label5.Text = exRollback.Message;

               }
           }
       }


   }
于 2012-11-05T07:59:33.300 回答
4

您还可以将事务包装到它自己的存储过程中并以这种方式处理它,而不是在 C# 本身中执行事务。

于 2008-10-22T07:40:01.077 回答
1

如果您只需要它用于与 db 相关的东西,一些 OR Mapper(例如 NHibernate)默认支持开箱即用的 transactinos。

于 2008-10-22T06:46:32.183 回答
0

这也取决于你需要什么。对于基本的 SQL 事务,您可以尝试在代码中使用 BEGIN TRANS 和 COMMIT TRANS 来执行 TSQL 事务。这是最简单的方法,但它确实具有复杂性,您必须小心正确提交(和回滚)。

我会使用类似的东西

SQLTransaction trans = null;
using(trans = new SqlTransaction)
{
    ...
    Do SQL stuff here passing my trans into my various SQL executers
    ...
    trans.Commit  // May not be quite right
}

任何失败都会让您立即退出,using并且事务将始终提交或回滚(取决于您告诉它做什么)。我们面临的最大问题是确保它始终承诺。使用确保事务的范围是有限的。

于 2008-10-22T06:51:20.660 回答