hcOPF - using Attribute OnChange Events

Althought hcOPF implements automatic calculations via a ThcCalcObject registered with the object metadata, it’s not the most efficient implementation.  Since the framework has no idea of the attribute dependancies in the calculation, it calls the CalcObject whenever an attribute of the object changes.  Of course it avoids doing so, during mass object attribute changes, such as when initializing the object, or reading it from the objectstore.  Nevertheless, you may encounter situations in which you want to optimize the calculation, such as when you make a database call.

Just like a TField object, a ThcAttribute implements an OnChange event that can be used implement calculations.  This is a much more efficient mechanism, but unfortunately does not benefit from the framework’s knowledge about the calculation, and cannot therefore automatically avoid triggering the calculation event during object reads or other mass object changes, such as object initialization or resetting object attributes after writing them to the objectstore.  It also suffers from the disadvantage that the code for multiple calculations is spread out across different event handlers instead of being in one place.  That said, any good framework does not box you in, so hcOPF allows you to use either method.

If you use the ThcAttribute event to perform the calculation, make sure to subscribe to the event early enough in the lifecycle of the object in an overridden method.  For instance, subscribing to the event for each object processed in the ThcObjectList.Load() method may be sufficient for most cases, but if you create individual objects for consumption, you should subscribe to the event in the ThcObject.Initialize method instead (recommended).  Also, be sure to check objectstate before performing the calculation.  Avoid trying to access attributes while they’re being initialized, or populated.  IOW, make sure the ObjectState = osNone and remember to fire the event in any ThcObject.Read() or ThcObject.ReadAttribute() override.

For example here is a possible event handler:

procedure TMyself.HairColorChanged(Sender :TObject);
begin
  if (ObjectState = osNone) then
  begin
    FQuery.SQL.Text := Format('select HairColor from fnRandomHairColor(%d)',
            [GetTickCount()]);
    FQuery.Open;
    try
      HairColor.Assign(FQuery.Fields[0]);
    finally
      FQuery.Close;
    end;
  end;
end;

and in the Read() override:

procedure TMyself.Read(Source :ThcObjectStore; WithChildren :boolean = True);
begin
  inherited Read(Source,WithChildren);
  HairColor.OnChange.FireEvent(HairColor);  //Sender should always be the attribute
end;

Tags: ,

Leave a Reply