How can I implement WCF Transaction support on custom class using CoreService?

旧时模样 提交于 2019-12-30 18:07:34

问题


I wrote a class to assist in adding & removing Destinations to a Publication Target using the Core Service. Destinations are normally exposed as a string (with XML content) via the Core Service, so I wrote my own wrappers around that, etc.

I now have a situation where I need to update 2 publication targets and thought it would be cool to use a transaction scope to ensure that both targets are updated at the same time.

I am however struggling with implementing this.

Code working (using standard CoreService WCF client):

TransactionOptions txOptions = new TransactionOptions 
                    { IsolationLevel = IsolationLevel.ReadCommitted };
using(TransactionScope scope = new TransactionScope(
                            TransactionScopeOption.Required, txOptions))
{
    PublicationTargetData publicationTarget1 = (PublicationTargetData)client.Read("tcm:0-1-65537", readOptions);
    PublicationTargetData publicationTarget2 = (PublicationTargetData)client.Read("tcm:0-2-65537", readOptions);

    publicationTarget1.TargetLanguage = "JSP";
    publicationTarget2.TargetLanguage = "JSP";
    client.Save(publicationTarget1, readOptions);
    client.Save(publicationTarget2, readOptions);

    // Stop saving
    scope.Dispose();
}

Executing this code will successfully roll back the changes I did (if I break before scope.Dispose() and check the publication targets in Tridion it successfully changes the target, and then "undoes" the change).

If I now try to use my "extended Publication Target" class also in a Transaction, I can't dispose it.

TransactionOptions options = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted };
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, options))
{
    ExtendedPublicationTarget target1 = new ExtendedPublicationTarget("tcm:0-1-65537");
    ExtendedPublicationTarget target2 = new ExtendedPublicationTarget("tcm:0-2-65537");
    target1.Destinations.Add(target1.Destinations[0]);
    target2.Destinations.Add(target2.Destinations[0]);
    target1.Save();
    target2.Save();
    scope.Dispose();
}

So basically, this is the question: What must I do to add transactionality to my .Save() method?

I have tried doing this:

[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
public void Save()
{
    _client.Save(targetData, readOptions);
}

But it didn't make a difference. Is there a way to determine if I am currently in a transaction and somehow "use" that transaction? I don't want to require a transaction, just want to have the option to operate in one.

Thanks, and sorry for the very long post... wanted to make sure I provided as much info as possible.


回答1:


The best resource for this is: WCF Transaction Propagation

You are missing at least one step. You also need to enable transactions in the binding:

<bindings>
   <netTcpBinding>
      <binding name = “TransactionalTCP” transactionFlow = “true” />
   </netTcpBinding>
</bindings>

Is there a way to determine if I am currently in a transaction and somehow "use" that transaction?

Yes. To determine if you are in a transaction you can check Transaction.Current. If you are in a transaction, you will use it unless you explicitly opt out. That's the beautiful/horrible thing about ambient transactions.

Figure 5 in WCF Transaction Propagation:

class MyService : IMyContract 
{
   [OperationBehavior(TransactionScopeRequired = true)]   
   public void MyMethod(...)
   {
      Transaction transaction = Transaction.Current;
      Debug.Assert(transaction.TransactionInformation.
                   DistributedIdentifier != Guid.Empty);
   } 
}

If Transaction.Current.TransactionInformation.DistributedIdentifier is empty, then the transaction is local and didn't "flow". Note that in a TransactionFlowOptions.Allowed configuration if the transaction fails to flow, it fails silently. So this really is the only way to check... and not flowing happens more easily than you would expect.

When I used tranactions for a production service I actually avoided TransactionFlowOptions.Allowed because the caller was never sure if the transaction actually flowed. If there was a binding configuration error in deployment, everything would run fine but rollbacks would fail... a very tendious error to detect. So I switched to required. Then a caller could ensure the transaction they provided was actually passed successfully. (If the transaction doesn't flow in a TransactionFlowOptions.Required configuration you'll get an exception.)



来源:https://stackoverflow.com/questions/12398027/how-can-i-implement-wcf-transaction-support-on-custom-class-using-coreservice

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!