Search code examples
databasedelphicalculated-fieldtdataset

Delphi - Change field to calculated field at runtime. Is this a good practice?


As in the question's title, I argue with a colleague about how calculated fields should be used. From my knowledge, calculated fields are created at runtime as in the François's answer on the question Adding a calculated field to a Query at run time. On the same question there is another answer, from sabri.arslan which suggest to change an existing field to a calculated one(code bellow)

var
 initing:boolean;

procedure TSampleForm.dsSampleAfterOpen(
  DataSet: TDataSet);
var
 i:integer;
 dmp:tfield;
begin
if not initing then
 try
  initing:=true;
  dataset.active:=false;
  dataset.FieldDefs.Update;
  for i:=0 to dataset.FieldDefs.Count-1 do
  begin
   dmp:=DataSet.FieldDefs.Items[i].FieldClass.Create(self);
   dmp.FieldName:=DataSet.FieldDefs.Items[i].DisplayName;
   dmp.DataSet:=dataset;
   if (dmp.fieldname='txtState') or (dmp.FieldName='txtOldState') then
   begin
     dmp.Calculated:=true;
     dmp.DisplayWidth:=255;
     dmp.size:=255;
   end;
  end;
  dataset.active:=true;
 finally
  initing:=false;
 end;
end;

procedure TSampleForm.dsSampleAfterClose(
  DataSet: TDataSet);
var
 i:integer;
 dmp:TField;
begin
if not initing then
begin
 for i:=DataSet.FieldCount-1 downto 0 do
 begin
  dmp:=pointer(DataSet.Fields.Fields[i]);
  DataSet.Fields.Fields[i].DataSet:=nil;
  freeandnil(dmp);
 end;
 DataSet.FieldDefs.Clear;
end;
end;

procedure TSampleForm.dsSampleCalcFields(
  DataSet: TDataSet);
var
 tmpdurum,tmpOldDurum:integer;
begin
  if not initing then
    begin
      tmpDurum := dataset.FieldByName( 'state' ).AsInteger;
      tmpOldDurum:= dataset.FieldByName( 'oldstate' ).AsInteger;
      dataset.FieldByName( 'txtState' ).AsString := State2Text(tmpDurum);
      dataset.FieldByName( 'txtOldState' ).AsString := State2Text(tmpOldDurum);
    end;
end;

procedure TSampleForm.btnOpenClick(Sender: TObject);
begin
 if dsSample.Active then
   dsSample.Close;
 dsSample.SQL.text:='select id,state,oldstate,"" as txtState,"" as txtOldState from states where active=1';
 dsSample.Open;
end;

I believe that this change leads to an unknown behavior of the specified TField. It is safe to change a dataset field to a calculated one on the runtime? What kind of issues can this generate?

LE: This is a question. Its purpose it is to demonstrate good practice on adding a calculated field on a dataset at runtime. And, yes adding a calculated field at runtime is bad design.

LE2: This is only an example for 'do not this in this way'. As an argument I asked what is the behavior of the field in discussion after doing this. How that field will act?


Solution

  • No, it´s not a good practice. The simple fact that the code is complex suggests that such a practice should be avoided. Someone already referenced the KISS principle and I agree with that.

    Particularly, the simple fact that the dataset has to be opened twice is enough to make me to dislike this practice.

    In addition, changing the field´s nature from data to calculated will change the way the dataset organizes the fields in its internal record representation (what the dataset calls a record buffer). Such a representation may be very different from one dataset implementation to another. Since the question didn´t identify a particular dataset, the changes in behavior are (in general):

    1. A data field will stored its value in a structure belonging to the underlying database client; a calculated field will stored its value in a not persistent buffer;
    2. During the dataset opening, there is a process named the field binding that consists to bind the data fields to the database client corresponding structure; when this binding fails, the dataset usually raises an exception; the calculated fields do not take part of this process because they use an internal field buffer to stored their value;
    3. The field, after becoming a calculated one, will accept values during the execution of OnCalcFields event in the way we are used to; it may not be used for filtering purposes, depending on the dataset implementation.

    However, a certain dataset implementation my present some other consequences, depending on its purpose and features.