ReEvaluate or ReSubscribe?

This article will explain derived members in ECO.  It's primary purpose is to describe the difference between the ReEvaluateSubscriber and ReSubscribeSubscriber parameters when you derive members in code.

One of the excellent features of Bold which have made the transition over into ECO is derived members.  A derived member is a bit like a calculated field on a dataset, except it is only calculated the first time you request its value and then that value is cached to processing time.

Take person's FullName as an example.  Full name would be calculated as

Result := Title + ' ' + FirstName + ' ' + LastName;

The first important step to creating such an attribute in ECO is to mark the attribute Derived.  It is actually possible to specify some "Derivation OCL" and have the ECO system derive everything for you

title + ' ' + firstName + ' ' + lastName

but this article is intended to show you how to handle much more complicated scenarios.

With a typical DataSet calculated field you would expect the method to be calculated every time the value is requested.  If the current object is being displayed as a list in a a DataGrid then calling this method for each instance every time the DataGrid repaints can be CPU intensive, especially if the value is calculated from many other attributes/objects.

In ECO the method is executed the first time the value is requested, but then the result will be cached automatically within the ECO space.  Future requests for the value of this derived member will return the cached value instead.  The potential for CPU saving here is obvious, especially with complicated routines.

A code-derived method for a member has a specific name + parameter list, ECO uses reflection to find the method.  The structure is

function <MemberName>DeriveAndSubscribe(ReEvaluateSubscriber,
  ReSubscribeSubscriber: ISubscriber): <MemberType>;

in our FullName example ECO would expect to find

function FullNameDeriveAndSubscribe(ReEvaluateSubscriber,
  ReSubscribeSubscriber: ISubscriber): string;

The problem here is this.  If the value of the Derived member is only calculated once, and then cached, what happens if the value of Title, FirstName, or LastName changes? 

The ECO framework obviously has no idea what we did to derive the value of this member, nor which other objects/members were used to calculate it.  To ensure that ECO never returns an incorrect value it needs to know when this member should be recalculated.  In fact, what it actually needs to know is when this member should be marked "Invalid", it wont be recalculated until its value is requested.

The ECO framework uses a kind of observer pattern to watch the relevant objects/members, whenever one of these are altered the EcoSpace will mark the derived member invalid (deriving a member via "Derivation OCL" will place these subscriptions automatically).  So, our code now looks something like this

function FullNameDeriveAndSubscribe(ReEvaluateSubscriber, ReSubscribeSubscriber: ISubscriber): string;
begin
  Result := Title + ' ' + FirstName + ' ' + LastName;
  //Now we place or "Subscriptions"
  Self.AsIObject().Properties['Title'].SubscribeToValue(ReEvaluateSubscriber);
  Self.AsIObject().Properties['FirstName'].SubscribeToValue(ReEvaluateSubscriber);
  Self.AsIObject().Properties['LastName'].SubscribeToValue(ReEvaluateSubscriber);
end;

The "ReEvaluateSubscriber" is an observer object which is passed to us from somewhere within the inner workings of the ECO framework.  All we need to do is to make sure we subscribe it to all of the relevant objects/members.  Whenever one of these dependant elements have their value altered, the ReEvaluateSubscriber will tell the ECO framework that the value of or member should be "ReEvaluated" next time it is requested.

ReEvaluate or ReSubscribe?
This brings me to the final point.  There are two subscribers passed to our method, "ReEvaluateSubscriber" and "ReSubscribeSubscriber", so what does the other one do, and when should it be used?

In the previous example we used the ReEvaluateSubscriber because we only needed to know when the values of Title, FirstName, or LastName change.  In a more complicated example we may need to do something more structurally complicated, like iterating through every OrderLine of a PurchaseOrder and subscribing to members of those related objects instead.

In this kind of situation, what happens if a new OrderLine is added?  We can't possibly already have subscribed to an object which yet hadn't existed at the point we placed our subscriptions?  What if an OrderLine is removed or deleted?

This is exactly what the ReSubscribeSubscriber is for.  This subscriber should be triggered whenever we want to say "Dump all of the subscriptions I placed last time, let's just start again".  Basically, it is not the "Values" of the elements which are changing, we need to replace our subscriptions because there are more/less objects to take into consideration.

function OrderValueDeriveAndSubscribe(ReEvaluateSubscriber,
  ReSubscribeSubscriber: ISubscriber): Decimal;
var
  CurrentOrderLine: OrderLine;
begin
  Result := 0;
  for CurrentOrderLine in Self.OrderLines do
  begin
    Result := Result + CurrentOrderLine.LineValue;
    //Subscribe to the member of the current child object
    CurrentOrderLine.AsIObject().Properties['LineValue'].SubscribeToValue(ReEvaluateSubscriber);

  end;
  //Finally, if the Self.OrderLines list changes in any way
  //we need to scrap all of our subscriptions and start again.
  Self.AsIObject().Properties['OrderLines'].SubscribeToValue(ReSubscribeSubscriber);
end;

Conclusion
Hopefully this has been an eye opener as to the abilities of code-derived members in ECO.  If you have not done so already I would recommend reading Rolf Lampa's article Dynamic navigation for higher performance, it was written for Bold, but the principles are the same.

 

Share this article!

Follow us!

Find more helpful articles: