本地事务

当要将多个任务绑定在一起,以便它们作为单个工作单元执行时,可以使用 ADO.NET 中的事务。 例如,假设应用程序执行两个任务。 首先使用订单信息更新表。 然后更新包含库存信息的表,将已订购的商品记入借方。 如果任何一项任务失败,两个更新均将回滚。

确定事务类型

如果事务是单阶段事务,并且由数据库直接处理,则属于本地事务。 如果事务由事务监视程序进行协调并使用故障保护机制(例如两阶段提交)解决,则属于分布式事务。

每个 .NET Framework 数据提供程序都使用自己的 Transaction 对象来执行本地事务。 如果要求事务在 SQL Server 数据库中执行,则选择 System.Data.SqlClient 事务。 对于 Oracle 事务,使用 System.Data.OracleClient 提供程序。 此外,还提供了一个新的 DbTransaction 类,用于编写需要事务并且与提供程序无关的代码。

备注

事务在服务器上执行时最为有效。 如果使用的 SQL Server 数据库广泛使用显式事务,应考虑使用 Transact-SQL BEGIN TRANSACTION 语句以存储过程的形式编写这些事务。

使用单个连接执行事务

在 ADO.NET 中,可以使用 Connection 对象控制事务。 可以使用 BeginTransaction 方法启动本地事务。 开始事务后,可以使用 Transaction 对象的 Command 属性在该事务中登记一个命令。 然后,可以根据事务组件的成功或失败,提交或回滚在数据源上进行的修改。

备注

不应对本地事务使用 EnlistDistributedTransaction 方法。

事务的作用域限于该连接。 以下示例执行显式事务,该事务由 try 块中两个独立的命令组成。 这两个命令对 AdventureWorks SQL Server 示例数据库中的 Production.ScrapReason 表执行 INSERT 语句,如果没有引发异常,则提交。 如果引发异常,catch 块中的代码将回滚此事务。 如果在事务完成之前事务中止或连接关闭,事务将自动回滚。

示例

按照下列步骤执行事务。

  1. 调用 BeginTransaction 对象的 SqlConnection 方法,以标记事务的开始。 BeginTransaction 方法返回对事务的引用。 此引用分配给在事务中登记的 SqlCommand 对象。

  2. Transaction 对象分配给要执行的 TransactionSqlCommand 属性。 如果在具有活动事务的连接上执行命令,并且尚未将 Transaction 对象配给 Transaction 对象的 Command 属性,则会引发异常。

  3. 执行所需的命令。

  4. 调用 Commit 对象的 SqlTransaction 方法完成事务,或调用 Rollback 方法结束事务。 如果在 CommitRollback 方法执行之前连接关闭或断开,事务将回滚。

以下代码示例演示使用 ADO.NET 和 Microsoft SQL Server 的事务逻辑。

using (SqlConnection connection = new(connectionString))
{
    connection.Open();

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

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

    try
    {
        // Execute two separate commands.
        command.CommandText =
          "INSERT INTO Production.ScrapReason(Name) VALUES('Wrong size')";
        command.ExecuteNonQuery();
        command.CommandText =
          "INSERT INTO Production.ScrapReason(Name) VALUES('Wrong color')";
        command.ExecuteNonQuery();

        // Commit the transaction.
        sqlTran.Commit();
        Console.WriteLine("Both records were written to database.");
    }
    catch (Exception ex)
    {
        // Handle the exception if the transaction fails to commit.
        Console.WriteLine(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.
            Console.WriteLine(exRollback.Message);
        }
    }
}
Using connection As New SqlConnection(connectionString)
    connection.Open()

    ' Start a local transaction.
    Dim sqlTran As SqlTransaction = connection.BeginTransaction()

    ' Enlist a command in the current transaction.
    Dim command As SqlCommand = connection.CreateCommand()
    command.Transaction = sqlTran

    Try
        ' Execute two separate commands.
        command.CommandText = _
          "INSERT INTO Production.ScrapReason(Name) VALUES('Wrong size')"
        command.ExecuteNonQuery()
        command.CommandText = _
          "INSERT INTO Production.ScrapReason(Name) VALUES('Wrong color')"
        command.ExecuteNonQuery()

        ' Commit the transaction
        sqlTran.Commit()
        Console.WriteLine("Both records were written to database.")

    Catch ex As Exception
        ' Handle the exception if the transaction fails to commit.
        Console.WriteLine(ex.Message)

        Try
            ' Attempt to roll back the transaction.
            sqlTran.Rollback()

        Catch exRollback As Exception
            ' Throws an InvalidOperationException if the connection 
            ' is closed or the transaction has already been rolled 
            ' back on the server.
            Console.WriteLine(exRollback.Message)
        End Try
    End Try
End Using

请参阅