Delphi - changing a field for a computed field at runtime. Is this a good practice?

As in the title of the question, I argue with a colleague on how to use calculated fields. To my knowledge, calculated fields are created at runtime, as in François's answer on the subject of Adding a calculated field to Query at runtime . On the same question, there is another answer from sabri.arslan, which suggests changing the existing field to a calculated one (below code below)

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 this change leads to unknown behavior of the specified TField. Is it safe to change the dataset field to a computed value at run time? What problems can arise?

LE: This is a question. Its purpose is to demonstrate good practice when adding a computed field to a dataset at runtime. And, yes, adding a computed field at runtime is a poor design.

LE2: This is just an example for "wrong." As an argument, I asked what is the behavior of the field in the discussion after this. How will this field work?

+4
source share
3 answers

No, this is not a good practice. The simple fact that the code is complex suggests that this practice should be avoided. Someone has already referred to the KISS principle, and I agree with that.

In particular, the simple fact that the dataset needs to be opened twice is enough to make me dislike this practice.

In addition, changing the nature of the field from data to calculated will change the way that the data set arranges the fields in the internal representation of the record (that the data set calls the write buffer). This representation can be very different from implementing one data set to another. Since the question did not identify a specific data set, changes in behavior (in general):

  • The data field will retain its value in the structure belonging to the base client of the database; the calculated field will save its value in a non-stable buffer;
  • When opening a data set, there is a process with the name of the field binding, which consists in binding the data fields to the corresponding structure of the database client; when this binding fails, the data set usually raises an exception; computed fields do not participate in this process because they use an internal field buffer to store their values;
  • The field, after calculation, will take on values ​​during the OnCalcFields event in the way we are used to; it cannot be used for filtering purposes, depending on the implementation of the data set.

However, the implementation of some data sets leads to some other consequences, depending on its purpose and capabilities.

+3
source

Has anyone noticed this bit of code?

 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; 

It does not change the database field to a calculated field ... It changes an unnecessary field to a calculated field. He knows the type of field ... it will be a string ... So this is a big deal ... No ... This is a hack ... Yes ... You can do the same in SQL using Cast ... Actually I have never seen a reason to use Calculated Fields ... I usually make the same thing easier in SQL.

I added more information after a little work, why not do it ...

sabri.arslan code ... for creating fields ... from FieldList ... also has problems with the lack of key settings and processing of hirchi fields.

 dmp:=DataSet.FieldDefs.Items[i].FieldClass.Create(self); 

Then we have Francois ...

 if you call the none hacked code you get this... for I := 0 to MyQuery.FieldDefList.Count - 1 do with MyQuery.FieldDefList[I] do if (DataType <> ftUnknown) and not (DataType in ObjectFieldTypes) and not ((faHiddenCol in Attributes) and not MyQuery.FIeldDefs.HiddenFields) then CreateField(Self, nil, MyQuery.FieldDefList.Strings[I]); 

umm ... Missing SetKeyFields will lead to unexpected behavior? Also, if the ObjectView property is set to True ... your dataset will not behave correctly for hierarchical fields ... It looks safer to just call Hack to CreateFields than use its code ... except that you have to be sure that your dataset component never calls this code ...

CreateField calls CreateFieldComponent and you get the result: = FieldClassType.Create (Owner) for your TField

Adapted from Borland's help TFieldDef "A field definition has a corresponding TField object, but not all TField objects have a corresponding field definition. For example, computed fields do not have field definition objects."

So, I ask you ... are you sure you can’t imagine the unknown behavior by creating CalculatedField on the fly? Are you sure that the fields have not yet been created or will not be created later? (There is an error in the sabri.arslan code because it opens / after opening ... It overwrites the original TFields ... I don’t understand why we need to recreate TField for an already open dataset)

So, what happens when CreateFields is called by a dataset (BDE and ADO do this on InternalOpen and make sure they are not values ​​in Fields ... all Dataset components do this the way? They don’t have to). What happens to the fields that you have already created ... are they overwritten? I did not see any code in TDataset or TFieldDef that checked whether TField was already created for the corresponding TFieldDef other than check the DefaultFields box (if the fields have a value).

+1
source

A TField is either mapped to a database column or not output and computed. This should be installed at design time. Any attempt to change this at runtime brings a bad IMO design, and you set yourself up for a lot of potential headaches.

0
source

Source: https://habr.com/ru/post/1496759/


All Articles