WCF Transaction Flow Question
I would like to configure WCF service operations to accept a transaction if my client sends one, but not to create one if none is sent. I think this would be similar to the Supported transaction attribute in COM+.
Does anyone know whether this can be accomplished with WCF transactions?
I am trying to accomplish this all by using the WCF transaction programming model (e.g. TransactionFlowAttribute and TransactionScopeRequired on service operations) rather than using the System.Transactions explicit transaction programming model.
Here's an example of why I think I want to be able to do this:
ServiceA implements a high-level business operation which invokes two data services. An operation in Service B does a single database update and an operation in Service C does two database updates.
ServiceA ----> ServiceB ----> <1 DB update>
| V Service C ----> <2 DB updates>ServiceC's 2 database updates need to be performed within a transaction with its root on ServiceC. Service B's single database update does NOT need to take place within a transaction. However, ServiceA defines a transaction that requires ServiceB and ServiceC's database updates two happen as an atomic unit of work.
My question is how I can configure ServiceB so that when it's invoked by Serv开发者_开发问答iceA it enlists in that transaction. However, when ServiceB is invoked directly rather than through Service A it does not need to run within a transaction because it's only doing one database update.
Thanks,
David Madrian
I don't think you can get the exact behaviour you wish.
The reason is that one Operation must either have TransactionScopeRequired be true or false. So TransactionScopeRequired is not equivalent to the COM+ transaction settings. If you wish to flow the transaction then TransactionScopeRequired must be true. However that means that the operation will always create a transaction when it is invoked.
Option 1
What you could do is to supress the transaction if there is no distributed transaction:
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete=false)]
public string GetData(int value)
{
using (TransactionScope scope = GetTransaction())
{
string result = DoSQL();
scope.Complete();
return result;
}
}
private TransactionScope GetTransaction()
{
Transaction ambientTransaction = Transaction.Current;
if (ambientTransaction == null
||
ambientTransaction.TransactionInformation.DistributedIdentifier.Equals(Guid.Empty))
{
return new TransactionScope(TransactionScopeOption.Suppress);
}
else
{
return new TransactionScope(ambientTransaction);
}
}
But that may violate your System.Transaction avoidance requirement. Plus it sort of smells. :)
Option 2
I know this isn't your question, but why can't service B always use a transaction? That would be my preference. It seems you don't want the overhead of a transaction since it is only one update call.
I can think of 2 reasons to have service B use a transaction:
- Architectural consistency -- all services follow the same pattern. This makes the system easier to understand and also...
- Maintainability -- if your system is easier to understand that also aids in maintainability. Also, if later you need to add a second update call to Service B the design already supports that with no changes.
Option 3
Another option would be to create 2 WCF Operations: one that is transactional and one that is not. The first could be called by transactional clients and the second when a transaction is deemed not necessary. The services themselves would delegate to the same method implementation so there would be no code duplication.
精彩评论