# 理解事务

商业应用程序经常需要在事务中运行脚本和组件。“事务”是一种服务器操作，无论此操作包含多少步骤（例如订购、检查存货以及付帐等），它都是要么全部成功，要么全部失败。可以创建在事务内运行的服务器端脚本，这样，如果脚本的某个部分出错，整个事务都将中止。

ASP 事务进程基于“组件服务”事务环境，这是一种事务处理系统，用来开发、部署和管理高性能、可扩展、稳固的企业、Internet 和 Intranet 服务器应用程序。此事务环境为开发基于组件的分布式应用程序定义应用程序编程模型。它还为部署和管理这些应用程序提供实时环境。

Web 服务器中嵌入了创建事务脚本所需的功能。如果安装了“组件服务”，也可以将组件打包以便在事务中运行。

## 关于事务

“事务”是要么成功，要么失败的一种操作。事务处理用于保证数据库更新的可靠性。当对数据库进行许多关联更改或同时更新多个数据库时，需要确保所有更改都准确执行。如果任何更改失败，则需要恢复到数据库表的原始状态。

如果没有“组件服务”，必须自己编写脚本和组件，以便手动跟踪请求的更改，并在更改失败时，手动恢复数据。有了“组件服务”，只需声明脚本和组件即可要求事务，并让“组件服务”处理事务间的协调。事务处理只适用于数据库访问，“组件服务”无法还原对文件系统或其他非事务资源的更改。应用程序所访问的数据库必须由“组件服务”支持。目前“组件服务”支持 SQL Server 和任何支持 X/Open 联盟 XA 协议的数据库。“组件服务”将在未来继续扩展对其他数据库的支持。

使用 Server.Transfer 和 Server.Execute 方法，事务可以扩展到多个 ASP 网页。如果脚本包含值为 Required 的 @TRANSACTION 指令，并且此脚本被 Server.Transfer 或 Server.Execute 方法调用，脚本也将继续运行调用 .asp 文件的事务（如果调用 .asp 文件已被处理）。如果调用 .asp 文件未被处理，调用的 .asp 文件将自动新建一个事务。

例如，下列脚本将初始化事务：

    <%@ TRANSACTION=Required %>

    <%
      .
      .
      .
      'End transaction.
      Server.Transfer("/BookSales/EndTrans.asp")
    %>

下列脚本将调用还可初始化事务的另一脚本：

    <%@ TRANSACTION=Required%>

    <%
      'Instantiate a custom component to close transactions.
      Set objSale = Server.CreateObject("SalesTransacted.Complete")
      .
      .
      .
    %>

不过，两个脚本的交互只能形成一个单一事务。有关编写带有 Server.Transfer 和 Server.Execute 的脚本的信息，请参阅将内容发送到浏览器。

## 声明事务脚本

如果声明某个网页是事务性的，则此网页中使用的所有脚本命令和对象都将在相同的事务环境中运行。具体的操作由“组件服务”处理，包括创建事务和确定事务是成功（提交）还是失败（中止）。要声明网页是事务性的，可在网页的最前面添加一条 @TRANSACTION 指令：

    <%@ TRANSACTION = value %>

关于 value 参数的详细信息，请参阅 @TRANSACTION 指令参考。

@TRANSACTION 指令必须位于页中的第一行，否则会产生错误。必须将此指令添加到要在事务中运行的所有页中。脚本处理完成后将结束当前事务。

多数应用程序只要求特定操作的事务环境。例如，航空公司站点可以使用事务脚本来售票和分配座位。所有其他脚本在没有事务环境的情况下也可安全运行。因为事务只适用于需要事务处理的网页，所以不可将应用程序的 Global.asa 文件声明为事务性的。

如果事务中止，“组件服务”将恢复对支持事务的资源所做的任何更改。目前只有数据库服务器完全支持事务，这是因为这些数据对企业应用程序来说是最重要的。“组件服务”无法恢复对硬盘文件、ASP 会话和应用程序变量或集合所做的更改。但是，可以编写脚本来恢复变量和集合，只需按本主题后面的说明编写事务事件即可。如果操作（如向文件写入数据）失败，脚本也可以显式提交或中止事务。

## 提交或中止脚本

因为“组件服务”跟踪事务处理，所以它将决定事务是完全成功还是完全失败。脚本可以显式声明它正在调用 ObjectContext.SetAbort 中止事务。例如，脚本可能在下列情况下中止事务：脚本收到来自组件的错误、违反了商业规则（例如，帐目余额降为负值）或者非事务操作（如读写文件）失败。如果事务完成前网页超时，事务也将中止。

## 编写事务事件

脚本本身无法确定事务是成功还是失败。不过，可以编写在事务提交或中止时可供调用的事件。例如，假设有一个贷记银行帐户的脚本，需要根据事务状态向用户返回不同的网页。可以使用 OnTransactionCommit 和 OnTransactionAbort 事件为用户提供各种响应。

    <%@ TRANSACTION=Required %>

    <%
      '缓冲输出以显示不同的网页。
      Response.Buffer = True
    %>

    <HTML>
      <BODY>
      <H1>欢迎使用联机银行服务</H1>

      <%
        Set BankAction = Server.CreateObject("MyExample.BankComponent")
        BankAction.Deposit(Request("AcctNum"))
      %>

      <P>谢谢！正在处理您的事务。</P>
      </BODY>
    </HTML>

    <%
      '如果事务成功完成，则显示此网页。
      Sub OnTransactionCommit()
    %>
      <HTML>
        <BODY>

        谢谢！您的帐户已获得信任。

        </BODY>
      </HTML>

    <%
      Response.Flush()
      End Sub
    %>

    <%
      '如果事务失败，则显示此网页。
      Sub OnTransactionAbort()
        Response.Clear()
    %>
      <HTML>
        <BODY>

        我们无法完成您的事务。

        </BODY>
      </HTML>
    <%
        Response.Flush()
      End Sub
    %>

## 在组件服务管理器中注册组件

要参与事务，组件必须在 COM+ 应用程序中注册，并配置为要求事务。例如，如果脚本通过调用一个更新库存数据库的组件和一个更新支付数据库的组件处理订单，则需要在一个事务环境中运行这两个组件。组件服务可以确保，如果一个组件失败，将返回整个订单，而不会更新任何数据库。一些组件并不要求事务；例如 Ad Rotator 组件就不需要事务。

可以使用组件服务管理器注册和配置事务组件。组件必须在 COM+ 应用程序中注册。不要将组件放在 IIS 进程内 COM+ 应用程序中；相反，应创建自己的 COM+ 应用程序。通常，应该将所有组件放在一个“库”应用程序中。“库”应用程序中的组件可用于多个 ASP 应用程序，并可以在 ASP 应用程序进程中运行。

也可以在“服务器”应用程序（通常在服务器的单独进程中运行的 COM+ 应用程序）中注册事务组件。如果要使用基于角色的安全性或允许远程计算机上的应用程序访问组件，可将“服务器”应用程序用于事务组件。

必须安装“组件服务”，才能使用“组件服务管理器”。

## 对象作用域

通常，不应该将根据 COM 组件创建的对象存储到 ASP Application 或 Session 对象中。事务完成后，COM 对象处于不活动状态。因为 Session 和 Application 对象是为可用于多个 ASP 网页的对象实例设计的，所以不能用它们保留那些将在事务结束时释放的对象。

ASP 脚本是已声明事务的根或起点。事务性 ASP 网页中使用的 COM 对象被当作是事务的一部分。事务完成后，网页中使用的 COM 对象处于不活动状态，包括 Session 或 Application 对象中存储的对象。随后，从其他事务性网页调用会话作用域或应用程序作用域的对象的尝试将失败。

## 队列事务

由于网络延迟或故障，对远程服务器上的数据库的更新可能会延迟或中止事务的完成。因为必须提交事务的所有部分，所以应用程序可能要一直等待远程服务器发出提交或中止消息，或者可能会因为不能发送数据库更新而中止事务。

对于必须同时完成的更新，适当的做法是中止甚至延迟事务的完成，直到事务的所有参与者都提交完毕。例如，机票订购应用程序必须同时完成在客户的银行帐号扣除机票金额和在航空公司的银行帐号增加机票金额。如果某个更新是事务的一部分，但它可能出现在其他更新之后，则不必让客户等到此更新完成。例如，订购机票的事务可能还要对食品服务商发出订餐请求或更新乘客的里程数。这些活动必须完成，但可以在以后完成。

可以使用“消息队列”将一个或一组更新捆绑成事务消息，然后发送给远程服务器。“消息队列”保证将更新发送到远程服务器，即使网络现在不可用。应用程序接收到提交消息后，即可继续事务。

© 1997-2001 Microsoft Corporation. 保留所有权利。

（下列单词只用于搜索，不会显示在网页中。） MSMQ