+ Start a Discussion
James@KronosLabJames@KronosLab 

Can't create objects in getter

I'm writing an application where I return a complex calculation as part of a getter on a page.  For efficiency, I'd like to cache the results in a group of objects, and only recompute the value if the underlying data becomes 'dirty'

Unfortunately, it seems that there is a DML limit of 0 on getters, because if I try to create any objects at all, I get a "Too many DML Statements: 1" error.

You might want to remove this restriction, as cached computations are a highly useful way to reduce resource requirements.

James
dchasmandchasman
The "bad" news: this is by design.

The "good" news: we provide a stateful programming model that makes
this kind of thing (along with multi request page user experiences (wizards for one) a snap to build). Instead of caching to the db you can store the expensive to calculate thing in a data member in your controller.

In your case it sounds like you might be looking for something less transient/shareable across users? I believe you can move your DML to your controller's constructor to avoid the no DML limit. You are thevfirst dev to provide a practical use case for lifting the restriction - not one I am particularly fond of either - stay tuned for an update on this...
James@KronosLabJames@KronosLab

I'll be a little more specific on the problem.

We're prototyping a timekeeping application.  Totalizing timecards is too expensive (> 20 queries) to run as a trigger when the hours are first imported, so we want to totalize and cache on first request (we mark the timecard as dirty if any of the underlying data is changed, so we know to totalize the next time the timecard is requested.)

The page in question is a reporting tool.  If we change the employee we're viewing, we want to retotalize the data.  The cleanest way to do this is to design a utilty function that anyone can call to get either the cached or retotalized data, and then have the getter call it.  But since getters can't create things, we can't cache the data back.  The constructor for the controller won't work, because we may change employee several times once the controller is instantiated.

I can work around it for the moment (assuming I can get action calls on onchange events to work.)  But it's a valid pattern to support.

James

mtbclimbermtbclimber


James@KronosLab wrote:

I'll be a little more specific on the problem...

... If we change the employee we're viewing, we want to retotalize the data.



Isn't that an event in the UI that should be calling the action to change/set the user context?



James@KronosLab wrote:

The cleanest way to do this is to design a utilty function that anyone can call to get either the cached or retotalized data, and then have the getter call it. 


Why is that cleaner? Isn't the utility function an action here?



James@KronosLabJames@KronosLab


mtbclimber wrote:

Isn't that an event in the UI that should be calling the action to change/set the user context?

Yes, in this case.  But what I'd really like to do is make a generic "get me the data, retotalizing if you need to" method, and call it whenever I need this data anywhere in the application.  Otherwise, the caller needed to know how the data is being calculated, which should be really be encapsulated.  The fact that asking for this value may result in database writes shouldn't need to be revealed to the caller.

Why is that cleaner? Isn't the utility function an action here?

See above.

 

James








dchasmandchasman
I don't disagree that this is a valid pattern and we have never really stopped discussing it in the VF team. This is a case of managing our feature's footprint/surface area mostly - only expose the things foks really need which is really the reason for having a dev preview like this in the first place :-)

One scenario I had envisioned that would also need DML in a getter is logging/audit trails. We bounced this thing around a number of times and decided to go out in Summer '07 with this restriction and planned to revisit if/when a real world need like yours materialized.
James@KronosLabJames@KronosLab


dchasman wrote:

In your case it sounds like you might be looking for something less transient/shareable across users? I believe you can move your DML to your controller's constructor to avoid the no DML limit. You are thevfirst dev to provide a practical use case for lifting the restriction - not one I am particularly fond of either - stay tuned for an update on this...


Nope, same zero DML restriction in the constructor...

James
dchasmandchasman
Sorry - I forgot to mention that DML in the ctor was only relaxed in Winter '08 - we also added support for a page level action binding (lets you specify an action method on your controller to be invoked on initial page get).