常见问题 - 嵌套调用操作失败,导致外围事务回滚
金蝶云社区-JohnnyDing
JohnnyDing
9人赞赏了该文章 1.1万次浏览 未经作者许可,禁止转载编辑于2016年03月29日 16:48:06
summary-icon摘要由AI智能服务提供

本文概述了K/3 Cloud中单据操作的流程,包括保存、删除、审核等操作在事务中的处理步骤,以及操作插件的事件触发机制。文章还讨论了嵌套操作可能引发的问题,如嵌套操作失败时外围事务的回滚及与数据库交互的禁止。同时,通过错误代码示例分析了嵌套操作导致事务状态无效的错误,并给出了正确处理此类错误的建议,即需要在调用提交、审核等操作后判断其是否成功,并在失败时及时抛出错误,避免后续操作因事务已回滚而中断。

背景说明

K/3 Cloud封装的单据保存、删除、审核、反审核等操作,会自动启动一个事务

操作的标准处理过程如下:

1. 进行操作校验;
2. 触发操作插件 BeforeExecuteOperationTransaction 事件;
3. 启动事务保护(如果外围已经启动了事务,则沿用外围事务)
4. 触发操作插件 BeginOperationTransaction 事件;
5. 执行保存、删除、审核、反审核等操作,对数据库的更新处理;
6. 触发操作插件 EndOperationTransaction 事件;
7. 提交事务
8. 触发操作插件 AfterExecuteOperationTransaction 事件。

如上,操作插件的 BeginOperationTransactionEndOperationTransaction 两个事件,是在事务中的。
如果在此事件中,调用了其他单据的保存、提交等操作,称为嵌套操作

嵌套操作可能引发的问题:

嵌套调用的操作,会直接沿用外围操作所启动的事务。

如果嵌套操作成功,代码会顺利的回到外围操作代码,随后提交整个事务,完成整个过程,万事大吉。

如果嵌套操作不成功,嵌套操作会自动回滚事务。
注意,此时因为嵌套操作,实际上使用的是外围操作的事务,回滚事务,其实回滚的是外围操作的事务。

回滚事务之后,与数据库的链接自动关闭,此时,要力图避免任何与数据库的交互。
如果此时尝试到数据库读取、存储数据,即会引发下面这个经典错误:

此操作对该事务的状态无效

附:错误反馈贴

https://vip.kingdee.com/article/37369
流程审核的时候,出现 此操作对该事务的状态无效

https://vip.kingdee.com/article/36345
销售出库单审核报错,麻烦谁遇见这个问题给看一下?

典型错误代码:

//******************************************************
///


/// 保存操作完毕,事务结束之前,进行自动提交、审核
///

///
public override void EndOperationTransaction(EndOperationTransactionArgs e)
{
// 取到需要自动提交、审核的单据内码
object[] pkArray = (from p in e.DataEntitys
select p[0]).ToArray();
// 设置提交参数
// using Kingdee.BOS.Orm;
OperateOption submitOption = OperateOption.Create();
submitOption.SetIgnoreWarning(this.Option.GetIgnoreWarning());
submitOption.SetInteractionFlag(this.Option.GetInteractionFlag());
submitOption.SetIgnoreInteractionFlag(this.Option.GetIgnoreInteractionFlag());



// 创建提交服务:using Kingdee.BOS.Contracts; using Kingdee.BOS.App;
ISubmitService submitService = ServiceHelper.GetService();
IOperationResult submitResult = submitService.Submit(
this.Context, this.BusinessInfo,
pkArray, "Submit", submitOption);



// 未判断提交是否成功,随后直接调用审核操作


// 构建操作可选参数对象
OperateOption auditOption = OperateOption.Create();
auditOption.SetIgnoreWarning(this.Option.GetIgnoreWarning());
auditOption.SetInteractionFlag(this.Option.GetInteractionFlag());
auditOption.SetIgnoreInteractionFlag(this.Option.GetIgnoreInteractionFlag());



// 构建单据主键参数
List> pkEntityIds = new List>();
foreach (var pkValue in pkArray)
{
pkEntityIds.Add(new KeyValuePair(pkValue, ""));
}
List paras = new List();
paras.Add("1");
paras.Add("");
// 调用审核操作
ISetStatusService setStatusService = ServiceHelper.GetService();

// 如下调用方式,需显示交互信息
IOperationResult auditResult = setStatusService.SetBillStatus(this.Context,
this.BusinessInfo,
pkEntityIds,
paras,
"Audit",
auditOption);



// 不判断审核是否成功,直接结束,使代码顺利往后执行
}
//*******************************************************

错误代码说明:

上述代码中,在操作插件的事务内事件EndOperationTransaction中,连续调用了单据的提交、审核操作。
但是并未判断调用的提交、审核操作是否成功。
如果提交操作失败,随后调用审核操作,即会因为外围事务已经回滚,与数据库的链接已经关闭,而引发中断。

此时,正确的做法判断提交操作状态,如果提交失败,即刻把失败原因通过抛出错误的方式给出,并结束流程。对审核操作的处理也一样。

正确处理代码:

请参阅下帖,重点关注其中对提交操作结果、审核操作结果的判断以及应对:

https://vip.kingdee.com/article/31955
知识共享 - 保存操作,同时进行自动提交、审核