WS 事务流

TransactionFlow 示例 演示了如何使用客户端协调的事务,以及如何在 WS-Atomic Transaction 或 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 操作请求不得包括流事务。

  • 由于省略 Divide 属性,TransactionFlow 操作请求不得包括流事务。

若要启用事务流,除了适当的操作属性外,还必须使用启用了 <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 接口定义包括设置为 TransactionFlowOptionNotAllowedTransactionFlowAttribute

  • 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. 若要生成解决方案的 C# 或 Visual Basic .NET 版本,请按照 生成 Windows Communication Foundation 示例中的说明进行操作。

  2. 确保已安装 SQL Server Express Edition 或 SQL Server,并且连接字符串已在服务的应用程序配置文件中正确设置。 若要在不使用数据库的情况下运行示例,请将服务的应用程序配置文件中的 usingSql 值设置为 false

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

    注意

    对于跨计算机配置,请使用以下说明启用分布式事务处理协调器,并使用 Windows SDK 中的 WsatConfig.exe 工具启用 WCF 事务网络支持。 有关设置 WsatConfig.exe 的信息,请参阅配置 WS-Atomic 事务支持

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

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

  1. 在运行 Windows Server 2003 或 Windows XP 的服务计算机上,按照以下说明配置 MSDTC 以允许传入的网络事务。

    1. “开始”菜单中,导航到 控制面板,然后 管理工具,然后 组件服务

    2. 展开“组件服务”。 打开 计算机 文件夹。

    3. 右键单击 “我的计算机” 并选择 属性

    4. 在“MSDTC”选项卡上,单击“安全配置”

    5. 选中“网络 DTC 访问”和“允许入站”

    6. 单击 “确定”,然后单击 “是” 以重启 MSDTC 服务。

    7. 单击 “确定” 关闭对话框。

  2. 在运行 Windows Server 2008 或 Windows Vista 的服务计算机上,按照以下说明配置 MSDTC 以允许传入的网络事务。

    1. “开始”菜单中,导航到 控制面板,然后 管理工具,然后 组件服务

    2. 展开“组件服务”。 打开 计算机 文件夹。 选择 分布式事务处理协调器

    3. 右键单击 DTC 协调器 并选择 属性

    4. 在“安全”选项卡上,选中“网络 DTC 访问”和“允许入站”

    5. 单击“确定”,然后单击“是”以重新启动 MSDTC 服务

    6. 单击 “确定” 关闭对话框。

  3. 在客户端计算机上,配置 MSDTC 以允许传出网络事务:

    1. 从“开始”菜单中,依次导航到 Control Panel“控制面板”、“管理工具”和“组件服务”

    2. 右键单击 “我的计算机” 并选择 属性

    3. 在“MSDTC”选项卡上,单击“安全配置”

    4. 选中“网络 DTC 访问”和“允许出站”。

    5. 单击“确定”,然后单击“是”以重新启动 MSDTC 服务

    6. 单击 “确定” 关闭对话框。