Reputation: 15159
I have a typical WCF 4.0 service that uses wsHttpBinding
with transactionFlow="true"
setting:
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
[FaultContract(typeof(Invalid))]
void DoWork();
and here is my server-side implementation:
[ServiceBehavior(
ConcurrencyMode = ConcurrencyMode.Multiple,
InstanceContextMode = InstanceContextMode.Single,
TransactionIsolationLevel = IsolationLevel.Serializable,
TransactionTimeout = "00:00:15",
ReleaseServiceInstanceOnTransactionComplete = false
)]
public class MyService: IMyService {
[OperationBehavior(TransactionScopeRequired = false)]
public void DoWork() {
if (System.Transactions.Transaction.Current == null)
Trace.TraceInformation("WTF?!");
}
}
and the client side call:
channel.DoWork(); // `Transaction.Current` is null at server-side as expected
using (var tx = new TransactionScope()) {
channel.DoWork(); // Transaction.Current is still null at server-side! :(
// .......
}
when I change TransactionScopeRequired
to true
I get my expected distributed transaction at server-side but now I get it even when calling without an outer scope:
channel.DoWork(); // Transaction.Current is not null
and what's most annoying it rolls back any of my inner completed scopes if I return Invalid
fault! That means I'm forced to always use explicit transaction scopes when calling my service despite TransactionFlowOption.Allowed
. Why is that?
I tried to add TransactionAutoComplete = false
to OperationBehavior
declaration in hope of fixing the issue but got
System.InvalidOperationException: TThe operation 'Create' on contract 'IUserIdentityStore' is configured with TransactionAutoComplete set to false and with ConcurrencyMode not set to Single. TransactionAutoComplete set to false requires ConcurrencyMode.Single.
which is just ridiculous.
In other words - how can I get optional transaction flow behavior and no auto-rollback when I return the declared fault?
Upvotes: 1
Views: 968
Reputation: 833
The TransactionScopeRequired documentation clearly states that if TransactionScopeRequired is false, the method will allways execute without a transaction.
As for the automatic rollback problem, if TransactionAutoComplete is set to true, all unhandled exceptions (Faults are Exceptions) will indeed rollback the transaction, which makes sense: there was a problem with this participant of a distributed transaction, thus the transaction must be rolled back. If you say the Invalid fault is not really a fault, then you should use another mechanism to indicate success or failure, like returning a result code from your service.
Next, your service definition seems a bit odd to me: InstanceContextMode is set to Single, which indicates your service will be a singleton: there will be only 1 instance of your service running. Furthermore, ConcurrencyMode is set to Multiple, which indicates that multiple clients can use that single instance at the same time.
I advise you to set InstanceContextMode to PerCall and ConcurrencyMode to Single, which will create a new dedicated service instance for every client call. There is an excellent article on WCF concurrency on Code Project.
Upvotes: 2