如何使用 CoreService 在自定义类上实现 WCF 事务支持? [英] How can I implement WCF Transaction support on custom class using CoreService?

查看:36
本文介绍了如何使用 CoreService 在自定义类上实现 WCF 事务支持?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一个类来帮助添加 &使用核心服务删除发布目标的目的地.目标通常通过核心服务公开为字符串(带有 XML 内容),因此我围绕它编写了自己的包装器,等等.

我现在有一种情况,我需要更新 2 个发布目标,并认为使用事务范围来确保同时更新两个发布目标会很酷.

然而,我正在努力实现这一点.

代码工作(使用标准 CoreService WCF 客户端):

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);//停止保存范围.处置();}

执行此代码将成功回滚我所做的更改(如果我在 scope.Dispose() 之前中断并检查 Tridion 中的发布目标,它会成功更改目标,然后撤消"改变).

如果我现在尝试在事务中也使用我的扩展发布目标"类,我无法处理它.

TransactionOptions options = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted };使用 (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();范围.处置();}

所以基本上,这是一个问题:我必须做什么才能将事务性添加到我的 .Save() 方法中?

我试过这样做:

[OperationContract][TransactionFlow(TransactionFlowOption.Allowed)]公共无效保存(){_client.Save(targetData, readOptions);}

但这并没有什么不同.有没有办法确定我当前是否处于交易中并以某种方式使用"该交易?我不想要求交易,只想有一个选项可以操作.

谢谢,很抱歉这篇很长的帖子......我想确保我提供了尽可能多的信息.

解决方案

对此的最佳资源是:WCF 事务传播

您至少遗漏了一步.您还需要在绑定中启用事务:

<netTcpBinding><绑定名称=TransactionalTCP"事务流=真"/></netTcpBinding></绑定>

<块引用>

有没有办法确定我当前是否在交易中并且以某种方式使用"该交易?

是的.要确定您是否在交易中,您可以检查 Transaction.Current.如果您正在进行交易,除非您明确选择退出,否则您将使用它.这就是环境交易的美妙/可怕之处.

WCF 事务传播中的图 5:

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

如果 Transaction.Current.TransactionInformation.DistributedIdentifier 为空,则事务是本地的并且没有流动".请注意,在 TransactionFlowOptions.Allowed 配置中,如果事务无法流动,则会静默失败.所以这确实是检查的唯一方法......并且不流动比您预期的更容易发生.

当我将交易用于生产服务时,我实际上避免了 TransactionFlowOptions.Allowed 因为调用者永远不确定交易是否真的流动.如果部署中存在绑定配置错误,一切都会正常运行,但回滚会失败……这是一个非常难以检测的错误.所以我切换到required.然后调用者可以确保他们提供的交易确实成功通过.(如果交易未在 TransactionFlowOptions.Required 配置中流动,您将收到异常.)

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.

解决方案

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.)

这篇关于如何使用 CoreService 在自定义类上实现 WCF 事务支持?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆