Acumatica 19R1中BQL生成SQL参数不匹配导致查询明细记录时出现运行时错误
问题背景
你在Acumatica 19R1里遇到了一个棘手的运行时错误:当尝试调取未用PXSelector的现有记录时,生成的SQL语句因为PXSelector和PXDBScalar的逻辑混合,加上DAC继承操作,出现了参数不匹配的问题。具体来说,子查询里错误地把XPMPaymentHdrAssocDoc.AssocDocID(字符串类型的单据号)绑定到了整数类型的@P1(客户ID),导致SQL抛出nvarchar转int失败的错误。而PXUnboundDefault能正常工作,但你需要在PXSelector的PXRestrictor里使用这个计算字段,所以PXUnboundDefault的方案没法直接满足需求。
问题根源
这个问题是Acumatica 19R1框架的一个已知bug:当PXDBScalar引用继承DAC的Current字段,且主查询中存在多个不同类型的Current引用时,框架的SQL参数绑定逻辑会混乱,把字符串类型的字段错误绑定到整数参数上。PXUnboundDefault之所以没问题,是因为它的计算是在内存层面处理的,不走SQL参数映射的流程。
解决方案
下面给你三个可行的方案,你可以根据项目情况选择:
方案1:用PXUnboundDefault+缓存事件适配Restrictor需求
既然PXUnboundDefault能正确计算值,我们可以通过缓存事件确保计算值在查询时被正确加载,这样就能在PXRestrictor里正常使用了:
- 修改XPMARRegister DAC:把
PXDBScalar替换回PXUnboundDefault,还可以加一个派生的计算字段方便Restrictor调用:
[System.SerializableAttribute()] [PXHidden] public class XPMARRegister : ARRegister { // 保留原有继承字段... public abstract class xPMPaymentHdrAssocDocUnrelasedPaymentAmount : PX.Data.IBqlField { } // 用PXUnboundDefault替代PXDBScalar,确保参数绑定正确 [PXUnboundDefault(typeof(Select5< XPMPaymentHdrAssocDoc, InnerJoin<XPMPaymentHdr, On<XPMPaymentHdr.paymentID, Equal<XPMPaymentHdrAssocDoc.paymentID>>>, Where<XPMPaymentHdrAssocDoc.assocDocType, Equal<Current<XPMARRegister.docType>>, And<XPMPaymentHdrAssocDoc.assocDocID, Equal<Current<XPMARRegister.refNbr>>, And<Where<XPMPaymentHdr.status, Equal<PaymentStatusList.applied>, Or<XPMPaymentHdr.status, Equal<PaymentStatusList.eligibleforSubmission>, Or<XPMPaymentHdr.status, Equal<PaymentStatusList.open>, Or<XPMPaymentHdr.status, Equal<PaymentStatusList.approved>>>>>>>>), Aggregate<Sum<XPMPaymentHdrAssocDoc.amount>>>), SourceField = typeof(XPMPaymentHdrAssocDoc.amount), PersistingCheck = PXPersistingCheck.Nothing] [PXDecimal(4)] public virtual Decimal? XPMPaymentHdrAssocDocUnrelasedPaymentAmount { get; set; } // 添加派生字段,用于Restrictor判断 public abstract class docBalLessUnreleased : PX.Data.IBqlField { } [PXDecimal(4)] [PXUnboundDefault(typeof(Sub<XPMARRegister.docBal, XPMARRegister.xPMPaymentHdrAssocDocUnrelasedPaymentAmount>))] public virtual Decimal? DocBalLessUnreleased { get; set; } }
- 在Graph中添加缓存事件:确保
XPMARRegister的计算字段在查询时被正确计算:
public class XPARInvoicePayment : PXGraph<XPARInvoicePayment, XPMPayAppHdr> { // 保留原有视图... public XPARInvoicePayment() { // 注册RowSelected事件,触发计算字段更新 this.Caches[typeof(XPMARRegister)].RowSelected += XPMARRegister_RowSelected; } private void XPMARRegister_RowSelected(PXCache sender, PXRowSelectedEventArgs e) { var row = e.Row as XPMARRegister; if (row == null) return; // 手动触发字段计算,确保派生字段值正确 sender.RaiseFieldUpdated<XPMARRegister.docBal>(row, row.DocBal); } }
- 更新Restrictor:现在可以直接用派生字段做判断了:
[PXRestrictor(typeof(Where<XPMARRegister.docBalLessUnreleased, Greater<decimal0>>), Messages.DocumentNotOpen)]
方案2:修改PXDBScalar的查询逻辑,强制参数绑定
如果必须用PXDBScalar,可以把Current替换成Required,强制框架为每个字段创建独立的参数,避免混淆:
[PXDBScalar(typeof(Search5< XPMPaymentHdrAssocDoc.amount, InnerJoin<XPMPaymentHdr, On<XPMPaymentHdr.paymentID, Equal<XPMPaymentHdrAssocDoc.paymentID>>>, Where<XPMPaymentHdrAssocDoc.assocDocType, Equal<Required<XPMARRegister.docType>>, And<XPMPaymentHdrAssocDoc.assocDocID, Equal<Required<XPMARRegister.refNbr>>, And<Where<XPMPaymentHdr.status, Equal<PaymentStatusList.applied>, Or<XPMPaymentHdr.status, Equal<PaymentStatusList.eligibleforSubmission>, Or<XPMPaymentHdr.status, Equal<PaymentStatusList.open>, Or<XPMPaymentHdr.status, Equal<PaymentStatusList.approved>>>>>>>>), Aggregate<Sum<XPMPaymentHdrAssocDoc.amount>>>))] [PXDecimal(4)] public virtual Decimal? XPMPaymentHdrAssocDocUnrelasedPaymentAmount { get; set; }
Required会告诉框架为对应的字段生成独立的SQL参数,不会和其他参数混淆。
方案3:升级Acumatica版本(最彻底的解决办法)
这个参数绑定的bug在Acumatica 20R1及以后的版本里已经被官方修复了。如果你的项目允许升级,直接升级到新版本就能彻底解决这个问题,不用改代码。
验证方法
- 应用方案后重新发布站点。
- 打开
XPARInvoicePayment页面,尝试选择关联单据,看是否还会报错。 - 用SQL Profiler监控生成的SQL,确认子查询里的
AssocDocID已经正确绑定到单据编号参数(@P3),而不是客户ID(@P1)。
内容的提问来源于stack exchange,提问作者user3708398

