如Juval Lowy的“编程WCF服务”中所述,WCF使用两阶段提交协议(2PC)来管理分布式事务:
当交易结束时,交易管理员检查参与服务的组合投票。如果任何服务或客户投票中止,则交易注定失败:指示所有参与资源放弃交易期间所做的更改。
我针对事务测试了不同的资源管理器(VRM,sql server,...),但它们都没有正常工作。主要问题是中止分布式事务不会回滚在资源上应用的更改。
My source由两个控制台程序(客户端和服务)组成。每次调用AddValue服务操作都会向sql-server数据库表添加一条记录(一个整数字段)。对AddValue的第二次调用会引发中止分布式事务的异常。
运行服务然后运行客户端程序。现在观察values3数组(在客户端程序中),它将是{1,2},这意味着更改不会回滚。
服务合同:
[ServiceContract(SessionMode=SessionMode.Required)]
public interface IExercise
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
void AddValue(int value);
[OperationContract]
int[] GetValues();
}
服务实施:
[ServiceBehavior(
InstanceContextMode = InstanceContextMode.PerSession,
ReleaseServiceInstanceOnTransactionComplete = false,
ConcurrencyMode = ConcurrencyMode.Single,
TransactionIsolationLevel = System.Transactions.IsolationLevel.Serializable,
TransactionTimeout = "00:5:0")]
[BindingRequirement(TransactionFlowEnabled = true)]
[ErrorHandlerBehavior]
public class ExerciseService : IExercise
{
#region IExercise
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
public void AddValue(int value)
{
Debug.Assert(Transaction.Current != null &&
Transaction.Current.TransactionInformation.DistributedIdentifier != Guid.Empty);
AddDbValue(value);
//AddVRMValue(value);
if (value == 2)
throw new Exception("abort tx");
//OperationContext.Current.SetTransactionComplete();
//Transaction.Current.Rollback();
}
public int[] GetValues()
{
var result = GetDbValues();
//var result = GetVRMValues();
return result;
}
#endregion
#region db
private const string Table1 = "dbo.Table1";
private const string Column1 = "Column1";
private static SqlConnection connection;
static ExerciseService()
{
var text = Properties.Settings.Default.ExerciseConnectionString;
connection = new SqlConnection(text);
connection.Open();
}
private void AddDbValue(int value)
{
var text = "insert into " + Table1 + " values (" + value + ")";
using (var command = new SqlCommand(text, connection))
{
command.ExecuteNonQuery();
}
}
private int[] GetDbValues()
{
using (var command = new SqlCommand("select * from " + Table1, connection))
using (var reader = command.ExecuteReader())
{
var result = reader.ReadColumn<int>(Column1).ToArray();
return result;
}
}
#endregion
#region VRM
private static TransactionalList<int> Storage = new TransactionalList<int>();
private void AddVRMValue(int value)
{
Storage.Add(value);
}
private int[] GetVRMValues()
{
var result = Storage.ToArray();
return result;
}
#endregion
}
服务代理:
public class ExerciseClient : ClientBase<IExercise>, IExercise
{
#region IExercise
public void AddValue(int value)
{
Channel.AddValue(value);
}
public int[] GetValues()
{
var result = Channel.GetValues();
return result;
}
#endregion
}
客户计划:
class Program
{
static void Main(string[] args)
{
try
{
using (var proxy2 = new ExerciseClient())
using (var proxy = new ExerciseClient())
{
using (var s = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromMinutes(5)))
{
proxy.AddValue(1);
var values = proxy.GetValues();
proxy2.AddValue(2);
var values2 = proxy2.GetValues();
s.Complete();
}
}
}
catch
{
}
using (var proxy3 = new ExerciseClient())
{
var values3 = proxy3.GetValues();
}
}
}
这是服务配置文件:
<services>
<service name="Tofigh.Exercise.ExerciseService">
<endpoint address="net.tcp://localhost/Exercise"
binding="netTcpBinding"
bindingConfiguration="bindingConfiguration"
contract="Tofigh.Exercise.IExercise"
/>
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="bindingConfiguration"
transactionFlow="true">
<reliableSession enabled="true" />
</binding>
</netTcpBinding>
</bindings>
我的第二个问题是,在事务中止的情况下,DTC如何调用RM来回滚更改?考虑RM生命周期依赖于服务生命周期的情况。在分布式tranasction结束之前,LTM或其他东西是否保存对资源的引用?如果当时资源不可用怎么办?