Yesterday turned into “one of those days”, followed by yet another “one of those days” today. The culprit… a 15 minute modification that ended up wasting too much of my day. Why, you may ask? Two little words, “I forgot.”
Most Acumatica developers have used SetValueExt at least a few times. In the documentation, it’s pretty straightforward. In most of my use-cases, it’s used in conjunction with a DAC extension. The root cause of my troubles turned out to be a simple syntax issue that was undetected by the compiler and unreported in a Trace. Before I can explain my error, let me first explain the proper use of SetValueExt.
INItemPlanExt planExt = plan.GetExtension<INItemPlanExt>();
Caches[typeof(INItemPlan)].SetValueExt<INItemPlanExt.usrXXMyField>(plan, "MyValue");
Caches[typeof(INItemPlan)].Update(plan);
Caches[typeof(INItemPlan)].Persist(PXDBOperation.Update);
In the code above, assume that plan is the current record of INItemPlan and that INItemPlan has a DAC extension to add a string field UsrXXMyField. Using SetValue and SetValueExt is a 3 step process.
- Set the value
- Update the cache
- Persist the cache
Steps 2 and 3 are easy-ish. Remember that you must update the row in the cache and persist the cache or else setting the value in the cache was a waste of time. Because of this, a FieldUpdated event needs only set a value via SetValueExt because the row will be updated when the event handlers reach RowUpdated. Otherwise, we must remember to update the row in the cache with step 2. Step 3 can be performed explicitly to persist the cache or via a Save.Press() call which will persist the cache for you.
So, what’s the big deal? Let’s break down the SetValueExt syntax, and then it will be very easy to explain my mistake.
Caches[typeof(INItemPlan)].SetValueExt<INItemPlanExt.usrXXMyField>(plan, "MyValue");
cache refers to WHICH cache. In an event handler, this might be sender of PXCache sender or e.Cache of Events.X<DAC.Field> e. In this example, we explicitly select the cache from the Caches object by looking up typeof(INItemPlan). We just as well could have called it Caches[“INItemPlan”], but I prefer error proofing syntax by returning the type of the specific DAC.
SetValueExt specifies that we want to set the value of the named DAC field <INItemPlanExt.usrXXMyField> AND call the event handlers that would fire when setting the value. To do so without calling event handers, just leave off the Ext part (use SetValue instead).
The first parameter, plan in this case, specifies the object that points to the row in the cache that we want to update. Notice that we are using plan in this case while setting the values in INItemPlanExt. The cached row will be located for plan. The cache contains all of the fields of the base DAC as well as the DAC extension. Therefore, once the cached row is located, usrXXMyField as defined in the DAC extension INItemPlanExt will be set within that cached row.
The second parameter, “MyValue” in this case, specifies what value to store into the field. This type of this value must match the type of the field being set, but no syntax errors for mismatches will be reported in the editor.
So, after all of that, what was my error today? The object passed in to locate the row in the cache. I passed planExt instead of plan. If planExt (INItemPlanExt) is an extension of plan (INItemPlan) then why does this matter? Well, planExt does not contain the key fields required to locate the record in the cache. No key fields means no match means failure to store the value.
Like many of you, I’m still learning. I hope to learn everyday, although maybe not in such a painful way as today. If you happen to see something here that you might explain better, please login and drop a comment below. I’ll be glad to update the post, but mostly I’ll be grateful for the chance to learn a little bit more.
Happy Coding!