How to call Persist of PXgraph without triggering the code written in that graph

Sometimes there is a need to call the PXGraph.Persist method from a graph extension but without triggering the code written in the base graph itself. I had this issue with using the ARSalesPriceMaint graph, so will show all the examples on that graph extension.

The problem is, it’s not possible to just call it, as it is not reachable from the GraphExtension level.

There was a solution by using reflection. Something like this:

public void Persist(Intercompany.PersistDelegate baseMethod)
{
    //base.Persist();
    MethodInfo fooA = typeof(PXGraph).GetMethod("Persist",
        BindingFlags.Public | BindingFlags.Instance, nullnew Type[] { }, null);
    DynamicMethod baseBasePersist = new DynamicMethod("foo_A",
        nullnew[] { typeof(PXGraph) }, typeof(PXGraph));
    ILGenerator il = baseBasePersist.GetILGenerator();
    il.Emit(OpCodes.Ldarg, 0);
    il.EmitCall(OpCodes.Call, fooA, null);
    il.Emit(OpCodes.Ret);
    baseBasePersist.Invoke(nullnew object[] { Base });
 
}

 

The problem with this solution is that first of all, any error messages thrown from the base Persist method will not appear on the screen instead of them only the “Exception has been thrown by the target of an invocation” appears. The second and the bigger issue is – all the caches will remain not cleared. It will cause some unexpected behavior, when the already deleted wrong record won’t allow to save the screen.

Since for the PXGraph.Persist we don't need specific graph logic, instead of calling the PXGraph from ARSalesPriceMaint base graph, we can create a completely empty new custom graph, then call it from our graph extension while passing all the caches and views. Additionally, we can even add some validations to the persist at the graph level.

This is the code example:

internal class ARSalesPriceMaintPersistHelper : PXGraph<ARSalesPriceMaintPersistHelper>
{
    public override void Persist()
    {
        MyValidation();
        base.Persist();
    }
 
    protected virtual void MyValidation()
    {
        PXTrace.WriteInformation("Overriden Validation is called");
    }
}
 
public class ext : PXGraphExtension<ARSalesPriceMaint>
{
    public delegate void PersistDelegate();
    [PXOverride]
    public void Persist(PersistDelegate baseMethod)
    {
        ARSalesPriceMaintPersistHelper ph = PXGraph.CreateInstance<ARSalesPriceMaintPersistHelper>();
        ph.Views = Base.Views;
        ph.Caches = Base.Caches;
        ph.Persist();
        Base.Cancel.Press();
    }
}

 The Graph was made internal to try to avoid its appearance in different graph selector in Acumatica, but it can also be just public.

 Summary

In addressing the challenge of invoking PXGraph.Persist in Acumatica from a graph extension, there is a nuanced solution beyond the initial reflection method. The initial approach, while innovative, led to issues with error messaging and cache management. Solution involves creating a new, custom graph specifically for handling the Persist method. This method ensures functional integrity and enables custom validations, effectively bypassing the limitations of the reflection-based approach. The article concludes with a practical code example, highlighting the new graph's internal designation to restrict its visibility in Acumatica.

 

How To Call Non Public Method Of Acumatica

 

Hello everybody,

today I want to share with you how it's possible to call some methods of Acumatica, which are not public, and which you don't want to copy/paste completely into your source code. In that case reflection will save you. Consider calling of InsertSOAdjustments method of graph SOOrderEntry below.

MethodInfo invokeSOAdjustment = typeof(SOOrderEntry).GetMethod(
    "InsertSOAdjustments"BindingFlags.Instance | BindingFlags.NonPublic, Type.DefaultBinder,
    new[] { typeof(SOOrder), typeof(ARPaymentEntry), typeof(ARPayment) }, null);
 
invokeSOAdjustment.Invoke(Base, new object[] { orderdocgraphpayment });

Also I want to give you a word of warning, that such approach potentially will not be certified, and another way of usage will be the one below:

In extension of SOOrderEntry create lines like those:

[PXOverride]
public void InsertSOAdjustments(SOOrder orderARPaymentEntry docgraphARPayment payment,
    Action<SOOrderARPaymentEntryARPaymentbaseAction)
{
    baseAction(orderdocgraphpayment);
}

and then just call InsertSOAdjustments method whenever you'll have a need for this.

Summary

Because Acumatica is written with C# which is very powerful language which gives you a lot of features you can easily achieve a lot of thigs, also be careful with usage of reflection. Somtime even more then Acumatica team anticipated themselves.