Acumatica中PXDBCalced用于PXRestrictor致AR303000栈溢出求助
问题背景
在CustomerExt扩展中添加了以下PXDBCalced字段,用于判断客户是否为父账户:
public abstract class isParent : PX.Data.BQL.BqlBool.Field<isParent> { } [PXBool] [PXUIField(DisplayName = "Is Parent")] [PXDBCalced(typeof(IIf< Exists<Select<BAccount, Where<BAccount.parentBAccountID, Equal<Customer.bAccountID>>>>, True, False>), typeof(bool))] public virtual bool? IsParent { get; set; }
随后在SOInvoiceExt图扩展中通过PXRestrictor使用该字段,阻止选择父客户:
[PXMergeAttributes(Method = MergeMethod.Append)] [PXRestrictor( typeof(Where<CustomerExt.isParent, Equal<False>>), "Account cannot be selected because it is a parent account.", typeof(Customer.acctCD))] protected void _(Events.CacheAttached<ARInvoice.customerID> e) { }
该限制可正常生效,但会导致客户维护页面(AR303000)出现栈溢出。推测存在循环引用,但无法理解为何该字段在SOInvoiceEntry的CustomerID及Customer GI中可正常运行,需寻找替代实现方式避免破坏AR303000页面。
替代实现方案
方案1:改用非计算字段,通过事件动态赋值
把IsParent从PXDBCalced改为普通PXBool字段,在Customer缓存的事件中计算赋值,避免数据库层面的循环查询:
1. 修改CustomerExt字段定义
public abstract class isParent : PX.Data.BQL.BqlBool.Field<isParent> { } [PXBool] [PXUIField(DisplayName = "Is Parent")] public virtual bool? IsParent { get; set; }
2. 在CustomerMaint扩展中添加事件赋值
public class CustomerMaint_Extension : PXGraphExtension<CustomerMaint> { protected void Customer_RowSelected(PXCache cache, PXRowSelectedEventArgs e) { Customer customer = e.Row as Customer; if (customer == null) return; // 查询是否存在子账户 bool hasChild = PXSelect<BAccount, Where<BAccount.parentBAccountID, Equal<Required<BAccount.bAccountID>>>> .Select(Base, customer.BAccountID).Any(); CustomerExt ext = cache.GetExtension<CustomerExt>(customer); ext.IsParent = hasChild; } }
3. 保留原PXRestrictor逻辑
原SOInvoiceExt中的PXRestrictor可以继续使用,因为字段值已通过事件动态计算,不会触发数据库层面的循环查询。
方案2:直接在PXRestrictor中使用Exists条件,不依赖计算字段
去掉IsParent计算字段,直接在PXRestrictor中内嵌判断逻辑,从根源避免Customer缓存层面的循环引用:
[PXMergeAttributes(Method = MergeMethod.Append)] [PXRestrictor( typeof(Where< Not<Exists<Select<BAccount, Where<BAccount.parentBAccountID, Equal<Current<ARInvoice.customerID>>>>>>), "Account cannot be selected because it is a parent account.", typeof(Customer.acctCD))] protected void _(Events.CacheAttached<ARInvoice.customerID> e) { }
这种方式不需要在CustomerExt中添加任何字段,直接在限制器中判断当前选中的客户是否有子账户,完全避开了可能引发循环的计算逻辑。
方案3:使用PXFormula替代PXDBCalced
如果需要保留字段用于显示(比如GI),可以改用PXFormula而非PXDBCalced——PXFormula是内存层面计算,不会触发数据库查询循环:
public abstract class isParent : PX.Data.BQL.BqlBool.Field<isParent> { } [PXBool] [PXUIField(DisplayName = "Is Parent")] [PXFormula(typeof(IIf< Exists<Select<BAccount, Where<BAccount.parentBAccountID, Equal<Customer.bAccountID>>>>, True, False>))] public virtual bool? IsParent { get; set; }
PXFormula仅在内存中计算字段值,不会像PXDBCalced那样在数据库查询时自动拼接SQL,从而避免AR303000页面加载时的循环查询栈溢出。
内容的提问来源于stack exchange,提问作者rjean99
相关产品推荐
相关产品推荐

