Skip to main content
Question

Base PXGraph extension and its side effects

  • April 2, 2026
  • 9 replies
  • 67 views

Hi Acumatica Developers,

If i have a field which is in several screens(say 10 screens). Instead of having 10 graph extension to implement custom logic.

Can i extend PXGraph like class “MyClassExt: PXGraphExtension<PXGraph>” and have events to implement my custom logic? 

Will there be any drawbacks with performance , locks, etc. ?
 

 

9 replies

kalima60
Freshman II
  • Freshman II
  • April 4, 2026

@naveenBCF Hi Naveen,
 

Yes, you can extend PXGraph globally, but it is highly discouraged if your goal is just to apply logic to 10 specific screens.

While doing public class MyClassExt : PXGraphExtension<PXGraph> is technically valid, here is a breakdown of why this approach is problematic, followed by the two best-practice alternatives for your scenario.

The Drawbacks of a Global PXGraph Extension
If you extend the base PXGraph, the Acumatica framework will forcefully inject your extension into every single graph instantiated across the entire ERP.

  1. Global Performance Penalty: Whenever a user logs in, opens a screen, runs a processing task, or triggers a REST/SOAP API call, Acumatica uses reflection to instantiate your extension and wire up its event handlers. Multiplying milliseconds across thousands of daily operations creates an unnecessary system-wide performance drag.
  2. Unintended Side Effects: Your logic may inadvertently fire in system processes, background threads, or integration APIs (like pushing data to the mobile app) where the cache happens to be modified. Managing concurrency, locks, and ensuring your code doesn't crash unrelated functionality becomes incredibly difficult.
     

Best Practice Alternatives
Instead of maintaining 10 separate, duplicated graph extensions or taking the risk of 1 global extension, you should use one of the two native Acumatica patterns below.

Alternative 1: Custom Event Subscriber Attribute (Best for Data/Field Logic)
If your custom logic is strictly tied to standard cache events (like FieldUpdated, FieldDefaulting, or RowSelected), the cleanest architectural approach is to encapsulate that logic into a custom Attribute. Attributes automatically execute their logic on any screen that uses the DAC field.

1. Create the custom Attribute code:

public class MyBusinessLogicAttribute : PXEventSubscriberAttribute, IPXFieldUpdatedSubscriber, IPXRowSelectedSubscriber
{
    public void FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
    {
        // Your logic here. 'sender' is the exact cache instance
        // belonging to whichever graph triggered the event.
    }

    public void RowSelected(PXCache sender, PXRowSelectedEventArgs e)
    {
        // Your UI enabling/disabling logic here
    }
}
2. Attach it to your DAC extension:
[PXDBString(50)]
[PXUIField(DisplayName = "My Cross-Screen Field")]
[MyBusinessLogic] // <--- Attach your custom logic here!
public virtual string UsrMyField { get; set; }
public abstract class usrMyField : PX.Data.BQL.BqlString.Field<usrMyField> { }
 

Why this is great: Zero repetitive graph extensions, perfectly isolated execution, and it is highly performant.

Alternative 2: Abstract Generic Graph Extension (Best for UI & Complex Logic)
If your logic is more complex—for example, you need to add custom Actions (buttons) to all 10 screens or you need to coordinate between multiple different DACs—you should use an abstract base class.

1. Write your shared logic once in an abstract generic class:

// The base class contains all the heavy lifting
public abstract class MySharedLogicExt<TGraph> : PXGraphExtension<TGraph> 
    where TGraph : PXGraph
{
    protected virtual void _(Events.FieldUpdated<MyDAC, MyDAC.usrMyField> e)
    {
        // Shared logic executed across all implementing screens
    }
    
    // You can even add shared Actions here!
    public PXAction<MyDAC> MyCustomButton;
    [PXButton, PXUIField(DisplayName = "Do Something")]
    protected virtual IEnumerable myCustomButton(PXAdapter adapter)
    {
        return adapter.Get();
    }
}
2. Create explicitly targeted "empty" derived extensions:

public class SOOrderEntry_SharedBaseExt : MySharedLogicExt<SOOrderEntry> { }

public class POOrderEntry_SharedBaseExt : MySharedLogicExt<POOrderEntry> { }

public class APInvoiceEntry_SharedBaseExt : MySharedLogicExt<APInvoiceEntry> { }
// ... etc. for the remaining 7 screens
Why this is great: You only write the code once, but the Acumatica framework only loads it into the 10 specific screens you explicitly declare, eliminating the global performance penalty.

Summary recommendation: Use Alternative 1 if you are just validating or defaulting data based on field changes. Use Alternative 2 if you are interacting heavily with the UI, pushing buttons, or spanning multiple Caches.


darylbowman
Captain II
Forum|alt.badge.img+16

This is a thorough answer with great recommendations


  • Author
  • Freshman I
  • April 7, 2026

Thank you, for taking time to provide thorough recommendations.
My scenario aligns with Alternative 2 , thought of implementing it .But curious to see if PXGraph extension or any similar possibility exist. Will Implement Generic Graph extension then. Thank you.


Samvel Petrosov
Jr Varsity III
Forum|alt.badge.img+9

Another option worth mentioning would be Generic Graph Extensions aka Reusable Business Logic

https://help.acumatica.com/Wiki/(W(5))/ShowWiki.aspx?pageid=bbc04253-7686-41dd-898b-aca47c1c6789


snikomarov36
Acumatica Employee
Forum|alt.badge.img
  • Acumatica Employee
  • April 8, 2026

Great answer with good details.
I would like to add just a couple of things from the platform POV.

First one is about the extensions on the PXGraph type. 

There are graph extensions on the base PXGraph type. So, this is not always wrong to use them. Acumatica itself uses this approach but very carefully and only for the case when the logic needs to be added to every screen in Acumatica. Not 10, not 20, to all of them including system maintenance screens. Usually, such extensions contain specific lower level logic that must be applied for every screen and we want to keep in a separate component. In this case, there downsides mentioned in the answer can’t be applied. There is no undesired performance downside since the logic has to be executed somewhere. And, though its difficult to predict all possible side effects in the system, it is expected that the extension and its effects will be applied to all graphs.

This is definitely not the scenario for business logic. It’s for system wide extension. Moreover, if you ever go this path, you will have to defend it during the ISV certification process (if you need to pass one).


snikomarov36
Acumatica Employee
Forum|alt.badge.img
  • Acumatica Employee
  • April 8, 2026

The second thing I want to add is about the alternative approaches.

Shared business logic in attributes has an advantage of being applied everywhere where the DAC is used. However, it has less capabilities for the customization of logic.

Currently, there is no way in Acumatica Framework to customize logic in the attribute placed on DAC fields and DACs in the same way as graph extensions. For example, two independent customizations that are not aware about each other can customize the same virtual method in a graph or graph extension and Acumatica will combine them.

The same can’t be achieved currently with attributes. You can’t clearly override a particular method in the attribute. Sure, you can replace attributes on DAC fields. But you can’t combine overrides of the same attribute from two or more customizations.

When you choose the approach, you need to estimate how frequently your solution will be customized. Will there ever be a need for such customization? I believe this can be important for big solutions.


  • Author
  • Freshman I
  • April 9, 2026

Thank you for the detailed explanation. 

What if the method to override is part of static class , which is being used in several graphs? 


snikomarov36
Acumatica Employee
Forum|alt.badge.img
  • Acumatica Employee
  • April 9, 2026

Hi ​@naveenBCF ,
AFAIK, In general, there is no direct way to override static methods. An this is expected, static method by definition is a method that has a static behavior which can’t be overridden.
If you believe that the static method is used improperly and you need to add an extension point to something in Acumatica, you should open a support case for that.

With that being said, you can try different workaround approaches like trying to override the methods that call the static helper and modify their behavior the way you need.

Finally, for the most difficult cases, there is also a technique in .Net world called IL weaving which allows you to do a lot of things that are not usually possible like overriding custom methods. There could be libraries based on this technique such as Harmony. However, I would not recommend using them in general and Acumatica won’t guarantee that the result will work and that it will be supported. 


  • Author
  • Freshman I
  • April 10, 2026

Thank you ​@snikomarov36  for valuable suggestions. Can close this topic.