WS 事务流

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

ms752261.note(zh-cn,VS.100).gif注意:
本主题的末尾介绍了此示例的设置过程和生成说明。

初始化到服务的连接和事务后,客户端访问多个服务操作。用于服务的协定定义如下,每个操作演示 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>
ms752261.note(zh-cn,VS.100).gif注意:
系统提供的 netTcpBinding 允许使用 transactionProtocol 的规范,而系统提供的 wsHttpBinding 仅使用互操作性更强的 WSAtomicTransactionOctober2004 协议。OleTransactions 协议仅供 Windows Communication Foundation (WCF) 客户端使用。

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

ms752261.note(zh-cn,VS.100).gif注意:
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");
ms752261.note(zh-cn,VS.100).gif注意:
无论选择哪种协议或传输,观察到的本示例的行为都是相同的。

启动到服务的连接后,客户端在对服务操作的调用附近创建新的 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. 若要生成 C# 或 Visual Basic .NET 版本的解决方案,请按照生成 Windows Communication Foundation 示例中的说明进行操作

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

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

    ms752261.note(zh-cn,VS.100).gif注意:
    对于跨计算机配置,请按照下面的说明操作来启用分布式事务处理协调器,并使用 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. 从**“开始”菜单中,依次定位到“控制面板”、“管理工具”“组件服务”**。

    2. 右击**“我的电脑”,并选择“属性”**。

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

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

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

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

ms752261.Important(zh-cn,VS.100).gif 注意:
您的计算机上可能已安装这些示例。在继续操作之前,请先检查以下(默认)目录:

<安装驱动器>:\WF_WCF_Samples

如果此目录不存在,请访问针对 .NET Framework 4 的 Windows Communication Foundation (WCF) 和 Windows Workflow Foundation (WF) 示例(可能为英文网页),下载所有 Windows Communication Foundation (WCF) 和 WF 示例。此示例位于以下目录:

<安装驱动器>:\WF_WCF_Samples\WCF\Basic\Binding\WS\TransactionFlow