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
接口定义包括设置为 TransactionFlowOptionNotAllowed
的 TransactionFlowAttribute。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 个操作(从第二个 Subtract
、Multiply
和 Divide
请求开始),因为客户端事务没有涉及这些操作。
设置、生成和运行示例
若要生成解决方案的 C# 或 Visual Basic .NET 版本,请按照 生成 Windows Communication Foundation 示例中的说明进行操作。
确保已安装 SQL Server Express Edition 或 SQL Server,并且连接字符串已在服务的应用程序配置文件中正确设置。 若要在不使用数据库的情况下运行示例,请将服务的应用程序配置文件中的
usingSql
值设置为false
。若要在单台计算机或跨计算机配置中运行示例,请按照 运行 Windows Communication Foundation 示例中的说明进行操作。
注意
对于跨计算机配置,请使用以下说明启用分布式事务处理协调器,并使用 Windows SDK 中的 WsatConfig.exe 工具启用 WCF 事务网络支持。 有关设置 WsatConfig.exe 的信息,请参阅配置 WS-Atomic 事务支持。
无论是在同一台计算机上运行示例还是在不同的计算机上,都必须配置 Microsoft 分布式事务处理协调器 (MSDTC),以启用网络事务流,并使用 WsatConfig.exe 工具启用 WCF 事务网络支持。
配置Microsoft分布式事务处理协调器(MSDTC)以支持运行示例
在运行 Windows Server 2003 或 Windows XP 的服务计算机上,按照以下说明配置 MSDTC 以允许传入的网络事务。
在 “开始”菜单中,导航到 控制面板,然后 管理工具,然后 组件服务。
展开“组件服务”。 打开 计算机 文件夹。
右键单击 “我的计算机” 并选择 属性。
在“MSDTC”选项卡上,单击“安全配置”。
选中“网络 DTC 访问”和“允许入站”。
单击 “确定”,然后单击 “是” 以重启 MSDTC 服务。
单击 “确定” 关闭对话框。
在运行 Windows Server 2008 或 Windows Vista 的服务计算机上,按照以下说明配置 MSDTC 以允许传入的网络事务。
在 “开始”菜单中,导航到 控制面板,然后 管理工具,然后 组件服务。
展开“组件服务”。 打开 计算机 文件夹。 选择 分布式事务处理协调器。
右键单击 DTC 协调器 并选择 属性。
在“安全”选项卡上,选中“网络 DTC 访问”和“允许入站”。
单击“确定”,然后单击“是”以重新启动 MSDTC 服务。
单击 “确定” 关闭对话框。
在客户端计算机上,配置 MSDTC 以允许传出网络事务:
从“开始”菜单中,依次导航到
Control Panel
“控制面板”、“管理工具”和“组件服务”。右键单击 “我的计算机” 并选择 属性。
在“MSDTC”选项卡上,单击“安全配置”。
选中“网络 DTC 访问”和“允许出站”。
单击“确定”,然后单击“是”以重新启动 MSDTC 服务。
单击 “确定” 关闭对话框。