Skip to main content
Answer

Poorly written data view delegate bypasses the Acumatica cache mechanisms

  • June 16, 2025
  • 6 replies
  • 212 views

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

In the course of attempting to add an unbound field to the grid of the ‘Critical Materials’ screen (CriticalMaterialsInq), what should have been a simple task turned into much more. Here’s my assessment of why, but I would love to hear some feedback from people who have a more thorough understanding of how the Acumatica cache works.

The grid is based on the ProdMatlRecs data view, made up of SelectedProdMatl records. The SelectedProdMatl records are created from AMProdMatl records and inserted into the cache in a data view delegate (prodMatlRecs()). Here’s where things get interesting.

If the cache contains records marked as inserted, these records are ‘yielded’ and that’s it

bool itVar1 = false;
IEnumerator enumerator = this.ProdMatlRecs.Cache.Inserted.GetEnumerator();
while (enumerator.MoveNext())
{
    SelectedProdMatl itVar2 = (SelectedProdMatl)enumerator.Current;
    itVar1 = true;
    yield return itVar2;
}

If there aren’t any ‘inserted’ records, the results are selected and constructed from AMProdMatl records, inserted, and then ‘yielded’:

if (!itVar1)
{
    foreach (/* records selected */)
    {
        var row = BuildSelectedProdMatl(prodMatlList, result, result, result, result, result, result, result, result, result);
        if (row != null)
        {
            prodMatlList.Add(row);
        }
    }

    foreach (var prodMatl in prodMatlList)
    {
     var isShort = prodMatl.QtyShort.GetValueOrDefault() > 0 && prodMatl.StatusID != ProductionOrderStatus.Completed;
        var isAllocated = prodMatl.IsAllocated == true;
        if (isShort || (current.ShowAll.GetValueOrDefault() && !isAllocated) || current.ShowAllocated.GetValueOrDefault() && isAllocated)
        {
            ProdMatlRecs.Insert(prodMatl);
            yield return prodMatl;
        }
    }
}

At the end, the cache is marked as ‘clean’:

this.ProdMatlRecs.Cache.IsDirty = false;

 

The problem with how this is written is that using cache functions like .SetValueExt and using event handlers don’t change what is displayed on the screen unless the grid is refreshed manually. Calling .View.RequestRefresh() was not enough (in my experience).

I ended up overriding the data view and delegate in order to do several things:

  • checked for (and returned) cached records instead of inserted
  • inserted and then set the cache status as Held
  • did NOT yield any records, but returned ProdMatlRecs.Cache.Cached instead

Now the cache mechanisms function normally and behaves as any ‘normal’ Acumatica screen.

 

I invite any criticism or feedback on whether this is the correct way a data view delegate should be written.

@snikomarov36 ​@Joshua Van Hoesen ​@Zoltan Febert ​@Keith Richardson ​@davidnavasardyan ​@harutyungevorgyan 

 

Best answer by darylbowman

I reevaluated this situation and realized that the only real issue with the original code seems to be this section:

foreach (var prodMatl in prodMatlList)
{
//...

ProdMatlRecs.Insert(prodMatl);
yield return prodMatl;
}

I changed it to:

foreach (var prodMatl in prodMatlList)
{
//...

yield return ProdMatlRecs.Insert(prodMatl);
}

I believe the difference is that prodMatl, in this case, is simply an object in a list and has no connection to the cache. The object returned by the insert is connected to the cache and therefore respects changes.

I reverted all my other changes and the behavior seems to be exactly what I needed.

6 replies

Dmitrii Naumov
Acumatica Moderator
Forum|alt.badge.img+7
  • Acumatica Moderator
  • June 16, 2025

I personally support the usage of Held over Inserted in this specific case. However, returning ‘cached’ makes me feel uneasy. For this screen it’s probably fine, but in general I’d think twice before using it. 


darylbowman
Captain II
Forum|alt.badge.img+15
  • Author
  • June 16, 2025

Could you explain your hesitancy?


Dmitrii Naumov
Acumatica Moderator
Forum|alt.badge.img+7
  • Acumatica Moderator
  • June 16, 2025

@darylbowman well, it does return ‘Deleted’ records and ‘InsertedDeleted’ too. So, I’d be careful with these.


harutyungevorgyan
Jr Varsity I
Forum|alt.badge.img+2

Hello ​@darylbowman ,

Thanks for bringing this up — and also, thank you for mentioning me!

Your approach will likely work fine and shouldn't cause issues with Cache.Cached. However, in these scenarios, I’ve found it most effective to look at how Acumatica itself uses the Framework for similar patterns.

A good example is the InventoryTranHistEnq graph. If you review its resultRecords() delegate, you’ll see that Acumatica constructs unbound InventoryTranHistEnqResult records, inserts them into the cache, and explicitly sets their status to "Held". The key difference is that instead of returning Cache.Cached, Acumatica returns a PXDelegateResult.

This pattern is consistent with how Acumatica treats virtual or inquiry-only records. I believe it's a solid practice to follow Acumatica’s native usage in such cases — it gives us a reliable and supportable approach when dealing with unbound data in inquiry screens.

Hope that helps!

 

 


darylbowman
Captain II
Forum|alt.badge.img+15
  • Author
  • June 17, 2025

@harutyungevorgyan - I’ve seen this before but never really digested what it was doing. I see it in quite a few source code files. Thanks for the tip.


darylbowman
Captain II
Forum|alt.badge.img+15
  • Author
  • Answer
  • June 19, 2025

I reevaluated this situation and realized that the only real issue with the original code seems to be this section:

foreach (var prodMatl in prodMatlList)
{
//...

ProdMatlRecs.Insert(prodMatl);
yield return prodMatl;
}

I changed it to:

foreach (var prodMatl in prodMatlList)
{
//...

yield return ProdMatlRecs.Insert(prodMatl);
}

I believe the difference is that prodMatl, in this case, is simply an object in a list and has no connection to the cache. The object returned by the insert is connected to the cache and therefore respects changes.

I reverted all my other changes and the behavior seems to be exactly what I needed.