WS 事务流

本示例演示客户端协调事务和使用 WS-Atomic 事务或 OleTransactions 协议的事务流的客户端和服务器选项的用法。本示例基于实现计算器服务的 入门示例,但对操作进行了属性化,以演示如何使用具有**“TransactionFlowOption”**枚举的 TransactionFlowAttribute 来确定事务流的启用程度。在流事务范围内,请求操作的日志将写入数据库并保存,直到客户端协调事务完成。如果客户端事务没有完成,则 Web 服务事务确保不提交对数据库的相应更新。

提示

本主题的最后介绍了此示例的设置过程和生成说明。

初始化到服务的连接和事务后,客户端访问多个服务操作。用于服务的协定定义如下,每个操作演示 TransactionFlowOption 的一种不同的设置。

[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface ICalculator
{
    [OperationContract]
    [TransactionFlow(TransactionFlowOption.Mandatory)]
    double Add(double n1, double n2);
    [OperationContract]
    [TransactionFlow(TransactionFlowOption.Allowed)]
    double Subtract(double n1, double n2);
    [OperationContract]
    [TransactionFlow(TransactionFlowOption.NotAllowed)]
    double Multiply(double n1, double n2);
    [OperationContract]
    double Divide(double n1, double n2); 
}

这按处理操作的顺序定义这些操作:

  • Add 操作请求必须包括流事务。
  • Subtract 操作请求可能包括流事务。
  • 由于显式 NotAllowed 设置,Multiply 操作请求不得包括流事务。
  • 由于省略 TransactionFlow 属性,Divide 操作请求不得包括流事务。

若要启用事务流,除了适当的操作属性以外,还必须使用启用了 <transactionFlow> 属性的绑定。在本示例中,除了元数据交换终结点以外,服务的配置还公开 TCP 终结点和 HTTP 终结点。TCP 终结点和 HTTP 终结点使用以下绑定,这两个绑定都启用了 <transactionFlow> 属性。

<bindings>
  <netTcpBinding>
    <binding name="transactionalOleTransactionsTcpBinding"
             transactionFlow="true"
             transactionProtocol="OleTransactions"/>
  </netTcpBinding>
  <wsHttpBinding>
    <binding name="transactionalWsatHttpBinding"
             transactionFlow="true" />
  </wsHttpBinding>
</bindings>

提示

系统提供的 netTcpBinding 允许使用 transactionProtocol 的规范,而系统提供的 wsHttpBinding 仅使用互操作性更强的 WSAtomicTransactionOctober2004 协议。OleTransactions 协议仅供 Windows Communication Foundation (WCF) 客户端使用。

对于实现 ICalculator 接口的类,所有方法的 TransactionScopeRequired 属性都设置为 true。此设置声明在方法内采取的所有操作都在事务范围内发生。在本例中,采取的操作包括记录到日志数据库。如果操作请求包括流事务,则操作发生在传入事务的范围内或自动生成新事务范围。

提示

TransactionScopeRequired 属性定义服务方法实现的本地行为,不定义客户端对事务进行流处理的能力或要求。

// Service class that implements the service contract.
[ServiceBehavior(TransactionIsolationLevel = System.Transactions.IsolationLevel.Serializable)]
public class CalculatorService : ICalculator
{
    [OperationBehavior(TransactionScopeRequired = true)]
    public double Add(double n1, double n2)
    {
        RecordToLog(String.Format(CultureInfo.CurrentCulture, "Adding {0} to {1}", n1, n2));
        return n1 + n2;
    }

    [OperationBehavior(TransactionScopeRequired = true)]
    public double Subtract(double n1, double n2)
    {
        RecordToLog(String.Format(CultureInfo.CurrentCulture, "Subtracting {0} from {1}", n2, n1));
        return n1 - n2;
    }

    [OperationBehavior(TransactionScopeRequired = true)]
    public double Multiply(double n1, double n2)
    {
        RecordToLog(String.Format(CultureInfo.CurrentCulture, "Multiplying {0} by {1}", n1, n2));
        return n1 * n2;
    }

    [OperationBehavior(TransactionScopeRequired = true)]
    public double Divide(double n1, double n2)
    {
        RecordToLog(String.Format(CultureInfo.CurrentCulture, "Dividing {0} by {1}", n1, n2));
        return n1 / n2;
    }

    // Logging method omitted for brevity
}

在客户端,针对操作的服务的 TransactionFlowOption 设置反映在客户端 ICalculator 接口的生成的定义中。而且服务的 transactionFlow 属性设置也反映在客户端的应用程序配置中。通过选择适当的 endpointConfigurationName,客户端可以选择传输和协议。

// Create a client using either wsat or oletx endpoint configurations
CalculatorClient client = new CalculatorClient("WSAtomicTransaction_endpoint");
// CalculatorClient client = new CalculatorClient("OleTransactions_endpoint");

提示

无论选择哪种协议或传输,观察到的本示例的行为都是相同的。

启动到服务的连接后,客户端在对服务操作的调用附近创建新的 TransactionScope

// Start a transaction scope
using (TransactionScope tx =
            new TransactionScope(TransactionScopeOption.RequiresNew))
{
    Console.WriteLine("Starting transaction");

    // Call the Add service operation
    //  - generatedClient will flow the required active transaction
    double value1 = 100.00D;
    double value2 = 15.99D;
    double result = client.Add(value1, value2);
    Console.WriteLine("  Add({0},{1}) = {2}", value1, value2, result);

    // Call the Subtract service operation
    //  - generatedClient will flow the allowed active transaction
    value1 = 145.00D;
    value2 = 76.54D;
    result = client.Subtract(value1, value2);
    Console.WriteLine("  Subtract({0},{1}) = {2}", value1, value2, result);

    // Start a transaction scope that suppresses the current transaction
    using (TransactionScope txSuppress =
                new TransactionScope(TransactionScopeOption.Suppress))
    {
        // Call the Subtract service operation
        //  - the active transaction is suppressed from the generatedClient
        //    and no transaction will flow
        value1 = 21.05D;
        value2 = 42.16D;
        result = client.Subtract(value1, value2);
        Console.WriteLine("  Subtract({0},{1}) = {2}", value1, value2, result);

        // Complete the suppressed scope
        txSuppress.Complete();
    }

    // Call the Multiply service operation
    // - generatedClient will not flow the active transaction
    value1 = 9.00D;
    value2 = 81.25D;
    result = client.Multiply(value1, value2);
    Console.WriteLine("  Multiply({0},{1}) = {2}", value1, value2, result);

    // Call the Divide service operation.
    // - generatedClient will not flow the active transaction
    value1 = 22.00D;
    value2 = 7.00D;
    result = client.Divide(value1, value2);
    Console.WriteLine("  Divide({0},{1}) = {2}", value1, value2, result);

    // Complete the transaction scope
    Console.WriteLine("  Completing transaction");
    tx.Complete();
}

Console.WriteLine("Transaction committed");

对操作的调用如下:

  • Add 请求将必需的事务流动到服务,服务的操作在客户端的事务范围内发生。
  • 第一个 Subtract 请求也将允许的事务流动到服务,服务的操作再次在客户端的事务范围内发生。
  • 第二个 Subtract 请求在用 TransactionScopeOption.Suppress 选项声明的新事务范围内执行。这会禁止客户端的初始外层事务,请求不会将事务流动到服务。此方法允许客户端显式放弃和防止将事务流动到服务(不需要流动时)。服务的操作发生在新的未连接的事务范围内。
  • Multiply 请求不会将事务流动到服务,因为客户端的生成的 ICalculator 接口定义包括设置为 TransactionFlowOption NotAllowedTransactionFlowAttribute
  • Divide 请求不会将事务流动到服务,因为客户端的 ICalculator 接口生成的定义再次没有包括 TransactionFlowAttribute。服务的操作再次发生在另一个新的未连接的事务范围内。

运行示例时,操作请求和响应将显示在客户端控制台窗口中。在客户端窗口中按 Enter 可以关闭客户端。

Starting transaction
  Add(100,15.99) = 115.99
  Subtract(145,76.54) = 68.46
  Subtract(21.05,42.16) = -21.11
  Multiply(9,81.25) = 731.25
  Divide(22,7) = 3.14285714285714
  Completing transaction
Transaction committed
Press <ENTER> to terminate client.

服务操作请求的日志记录显示在服务的控制台窗口中。在客户端窗口中按 Enter 可以关闭客户端。

Press <ENTER> to terminate the service.
  Writing row to database: Adding 100 to 15.99
  Writing row to database: Subtracting 76.54 from 145
  Writing row to database: Subtracting 42.16 from 21.05
  Writing row to database: Multiplying 9 by 81.25
  Writing row to database: Dividing 22 by 7

成功执行示例后,客户端的事务范围完成并提交在该范围内采取的所有操作。特别是,上面提到的 5 个记录保留在服务的数据库中。其中的前 2 个记录已在客户端的事务范围内发生。

如果在客户端的 TransactionScope 内的任意位置发生异常,则事务无法完成。这将导致在该范围内写入日志的记录不会被提交到数据库。注释掉完成外层 TransactionScope 的调用后重复运行该示例可以观察到此效果。当如此运行时,只记录后 3 个操作(来自第二个 SubtractMultiplyDivide 请求),因为客户端事务不流到这些操作。

设置、生成和运行示例

  1. 确保已安装 SQL Server 2005 Express Edition 或 SQL Server 2005。在服务的 App.config 文件中,可以设置数据库 connectionString,或者通过将 appSettings usingSql 值设置为 false 而禁用数据库交互。

  2. 若要生成 C# 或 Visual Basic .NET 版本的解决方案,请按照生成 Windows Communication Foundation 示例中的说明进行操作。

  3. 使用 Windows SDK 中的 WsatConfig.exe 工具可启用 WCF 事务网络支持。

  4. 若要用单机配置或跨计算机配置来运行示例,请按照运行 Windows Communication Foundation 示例中的说明进行操作。

无论是在同一计算机上运行示例,还是在其他计算机上运行示例,都必须配置 Microsoft 分布式事务处理协调器 (MSDTC),以启用网络事务流并使用 WsatConfig.exe 工具启用 WCF 事务网络支持。

配置 Microsoft 分布式事务处理协调器 (MSDTC) 以支持运行示例

  1. 在服务计算机上,配置 MSDTC 以允许传入网络事务。

    1. 从**“开始”菜单中,依次定位到“控制面板”“管理工具”“组件服务”**。
    2. 打开**“计算机”**文件夹。
    3. 右击**“我的电脑”,并选择“属性”**。
    4. 在**“MSDTC”选项卡上,单击“安全配置”**。
    5. 选中**“网络 DTC 访问”“允许入站”**。
    6. 单击**“确定”,然后单击“是”**以重新启动 MSDTC 服务。
    7. 单击**“确定”**关闭对话框。
  2. 在服务计算机和客户端计算机上,配置 Windows 防火墙以便在例外应用程序列表中包括 Microsoft 分布式事务处理协调器 (MSDTC):

    1. 从“控制面板”上运行 Windows 防火墙应用程序。
    2. 从**“例外”选项卡上,单击“添加程序”**。
    3. 浏览到文件夹 C:\WINDOWS\System32。
    4. 选择 Msdtc.exe 并单击**“打开”**。
    5. 单击**“确定”关闭“添加程序”对话框,然后再次单击“确定”**关闭 Windows 防火墙小程序。
  3. 在客户端计算机上,配置 MSDTC 以允许传出网络事务:

    1. 从**“开始”菜单中,依次定位到Control Panel**、“管理工具”“组件服务”
    2. 右击**“我的电脑”,并选择“属性”**。
    3. 在**“MSDTC”选项卡上,单击“安全配置”**。
    4. 选中**“网络 DTC 访问”“允许出站”**。
    5. 单击**“确定”,然后单击“是”**以重新启动 MSDTC 服务。
    6. 单击**“确定”**关闭对话框。

Send comments about this topic to Microsoft.
© 2007 Microsoft Corporation. All rights reserved.