Implementing foxpro's scatter and gather memvar in delphi
Implementing FoxPro's Scatter and Gather Memvar in Delphi.
Delphi Developer February 2001
Copyright Pinnacle Publishing, Inc. All rights reserved.
Implementing FoxPro's Scatter and Gather Memvar in Delphi
Steve Zimmelman
Technology is always moving forward, usually leading to more productive development. However, there are times when reaching into the past can be helpful, too. Replicating or comparing table data in FoxPro was a fairly simple task. Steve Zimmelman argues that perhaps this technology is worth reaching back.
FoxPro's Scatter and Gather commands were always useful for copying records from one table to another, comparing data, or record replication among multiple tables. For those of you who haven't had the pleasure of working with FoxPro, the Scatter and Gather commands work like this.
Suppose you need to copy a record from one table to another. You'd do something like:
Select Table1
Scatter Memvar
Select Table2
Gather Memvar
The Scatter command reads all of the fields for the current
record and places them into memory variables. Each variable that's
created with Scatter has the same name as the field from which it
originated. The Gather command updates the current record with memory variables that have names that correspond to the fields. So if Table1 has the fields Name, Address, and Phone, and the variables Name, Address, and Phone exist, then when the Gather command is executed it updates the record with the value stored in those variables.
There are many ways to accomplish this with Delphi, but there's nothing natively supplied in the VCL that works at the TDataSet level. In my latest application endeavor, I had a need for the old Scatter and Gather functionality. So, I ended up writing my own ScatterMemvar and GatherMemvar methods.
Creating the Scatter and Gather functions
Both of these methods are wrappers for an object called TMemvar. TMemvar maintains a dynamic array of a record called TMemVarFields. Each element of the MemVarFields array contains the FieldName, DataType, and Value of a corresponding field in a DataSet. So each instance of TMemvar contains the data from a single table record.
Type
MemVarField = Packed Record
FieldName : String;
Value : Variant ;
DataType : TFieldType;
End;
TMemVarFields = Array Of TMemVarField ;
When the ScatterMemvar function is invoked, it creates an instance of TMemvar and returns a pointer to that instance. When TMemvar is created, it loads the fields from the designated DataSet into the array. To avoid DataSet duplication, each instance of TMemvar is identified by its DataSet property. Before a new instance of TMemvar is created, a search is performed for a matching DataSet in all existing instances of TMemvar. If one is found, the matching instance is reused. If not, a new instance is created.
Function ScatterMemvar(DataSet:TDataSet) : TMemVar ;
Begin
If (DataSet = Nil) Then
Raise Exception.Create('DataSet Cannot Be NIL');
// Reuse the object if it already exists.
Result := FindMemvarObject(DataSet);
If (Result = Nil) Then Begin
Result := TMemvar.Create(DataSet) ;
End Else Begin
Result.LoadFieldData ;
End;
End;
Constructor TMemVar.Create(DataSet:TDataSet) ;
Begin
FFlds := Nil ;
FDataSet := DataSet ;
If (FDataSet = Nil) Then Begin
Raise Exception.Create('DataSet Cannot Be NIL');
End;
If (FScatterList = Nil) Then
FScatterList := TList.Create ;
FScatterList.Add(Self);
LoadFieldData ;
End;
Procedure TMemVar.LoadFieldData ;
Var
i : Integer ;
Begin
If Not DataSet.Active Then DataSet.Open ;
FFlds := Nil ;
// set the length for the record array.
SetLength(FFlds,FieldCount);
For i := 0 To (DataSet.FieldCount-1) Do Begin
With DataSet.Fields[I] Do Begin
FFlds[i].FieldName := FieldName ;
FFlds[i].Value := Value;
FFlds[i].DataType := DataType ;
End;
End;
End;
Normally when you create an object, it's necessary to free the object when it's no longer needed. But the unit that contains the code for this object maintains its own internal list of TMemvar instances and destroys them automatically when the application closes. So you can free the objects when they aren't needed, or ignore them and let the unit take care of itself.
To manage the TMemvar objects that are created for each ScatterMemvar usage, I add each new instance of TMemvar to FScatterList, a type of TList. The unit that handles all of this code contains Initialization and Finalization sections. The Initialization makes sure FScatterList is set to Nil. This is done so that the Create method of TMemvar can interrogate the value of FScatterList and create an instance of it only when necessary. This keeps the resources that the unit requires down to a..